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
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 (
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.