Interesting query pattern for repeatedly bisecting lists

I use an Elm-powered CLI to plan work using an algorithm similar to Mark Forster’s Final Version system. This algorithm relies heavily on bisecting a list—it’s all about selecting the first inactive todo before all active todos, that sort of thing.

Prior to today, I was doing this with a lot of List work, which I was not happy with. it obscured my intent in a lot of case matching.

But as of today, I’ve made a DSL that makes performing the required bisections pretty nice. Using it ends up looking like this:

startSelecting : Todos -> Maybe ( Todos, Todo )
startSelecting todos =
    todos
        |> slice
        |> before First Filter.isWorkable
        |> last Filter.isSelectable
        |> map Todo.activate

I’m really happy with this! It’s way better than the deep nesting I was having to do before. Here’s how the API looks:

type Slice quantity -- opaque
slice : Todos -> Slice Multiple

type Multiple -- opaque
type Position = First | Last
before : Position -> Filter -> Slice Multiple -> Slice Multiple
after : Position -> Filter -> Slice Multiple -> Slice Multiple
getList : Slice Multiple -> List Todo

type Single -- opaque
first : Filter -> Slice Multiple -> Slice Single
last : Filter -> Slice Multiple -> Slice Single
get : Slice Single -> Maybe Todo
map : (Todo -> Todo) -> Slice Single -> Maybe ( Todos, Todo )

I’m particularly proud that the types here prevent us from doing the wrong terminal query (get vs getList) if the list is insufficiently narrowed or over-narrowed. This is currently pretty specific to my code (e.g. I never need to modify more than one todo at once, so I don’t.) It could be made more general, like this:

type Slice a quantity -- opaque
slice : List a -> Slice a Multiple

type Multiple a -- opaque
type Position = First | Last
before : Position -> (a -> Bool) -> Slice a Multiple -> Slice a Multiple
after : Position -> (a -> Bool) -> Slice a Multiple -> Slice a Multiple
getList : Slice a Multiple -> List a

type Single a -- opaque
first : (a -> Bool) -> Slice a Multiple -> Slice a Single
last : (a -> Bool) -> Slice a Multiple -> Slice a Single
get : Slice a Single -> Maybe a
map : (a -> a) -> Slice a Single -> Maybe ( List a, a )

If this pattern matches some work that you’re currently doing, the (non-generic) code is here: https://github.com/BrianHicks/daily/blob/61372bc7c851d686fe6917bfbe78d12505e5879d/src/Slice.elm If this turns out to be helpful for someone else, I’m happy to make it into a package so we can both use it. :slight_smile:

1 Like

This link seems to return a 404: https://github.com/BrianHicks/daily/blob/61372bc7c851d686fe6917bfbe78d12505e5879d/src/Slice.elm
Maybe the repo is private?

haha, whoops! You’re right. I thought I had made it public, but now see I haven’t done some essential steps which I don’t have time for right now (like selecting a LICENSE, writing README.md, scrubbing private info, etc.) Here’s a gist with the file:

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.