This is absolutely composition in the Maybe monad, or really the Maybe Kleisli category. The author probably knows this, but I’ll make it clear here.
Each of the “accessor functions” has a signature a bit like this: a -> Maybe b since we may fail to find the inner target due to it being undefined. The Kleisli category is a category structure which is induced by arrows into monads:
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
instance Monad m => Category (Kleisli m) where
id = Kleisli return
Kleisli f . Kleisli g = Kleisli (\a -> g a >>= f)
Now, we can build chains of Kleisli Maybe arrows
type a ~> b = Kleisli Maybe a b
get_msg :: Delivery ~> Msg
get_content :: Msg ~> Content
get_props :: Content ~> Props
get_headers :: Props ~> Headers
get_all :: Delivery ~> Headers
get_all = get_headers . get_props . get_content . get_msg
Now the Erlang example uses a list of these “accessors” but that will fail for us because basic lists cannot store heterogenous types. It’d be okay if each of the get_* methods were “Kleisli endomorphisms”, e.g. had the same type a ~> a for some a, but they don’t.
We can make a list structure which is able to store “paths” of arbitrary Kleisli arrows, but it turns out that if all you’re interested in is the final, composed result then there isn’t a whole lot of value in doing this. You end up writing the same thing each way: what’s the difference between foldl (.) id [a, b, c, d] and a . b . c . d, really?
(Oh, it should also be stated that lenses are really closely associated with this idea. In this case, we’re talking about a generalization of lenses sometimes known as prisms, though.)
I know about the Maybe monad, that’s why I have penultimate paragraph comment :)
I haven’t read much about Kleisli
I think your composition comment at the end is stated in “A tutorial on the universality and expressiveness of fold”, but sadly Erlang won’t allow this kind of syntax
Yep, I hope I didn’t insinuate you weren’t aware of it at all. Just didn’t want to assume too much.
The idea of replacing Cons a (Cons b (Cons c Nil) with f a (f b (f c zero) absolutely falls out of the universality of foldr for lists. In this case you can’t quite do that since Cons get_msg (Cons get_content Nil) is not well-typed. We can do something like this though with a “free category” construction
data FCat f a b where
Nil :: FCat a a
Cons :: f a x -> FCat x b -> FCat a b
and now Cons get_msg (Cons get_content (Cons get_props (Cons get_headers Nil))) is well-typed. We also have a fold principle
fold :: (forall a x . f a x -> r x b -> r a b) -> r b b -> (FCat f a b -> r a b)
fold cons nil = \case
Nil -> nil
Cons arr cat -> cons arr (fold cons nil cat)
and this one has similar universal properties in that fold f z transforms
Cons q (Cons r (Cons s Nil)) ===> f q (f r (f s z))
I just want to say that I appreciate not assuming and the nice description. I just wanted to say that I had remembered an elixir article posted about railway programming and there was a chain in the comments about why he didn’t talk about it as a maybe monad. It then turned into, “because monad is a scary word,” when in a response to a comment on the article the author said he wasn’t familiar with monads.
Just want to say that I liked the way you introduced it then gave an explanation to. Make it clear to those who didn’t know exactly what you meant (like me).
This is absolutely composition in the
Maybemonad, or really theMaybeKleisli category. The author probably knows this, but I’ll make it clear here.Each of the “accessor functions” has a signature a bit like this:
a -> Maybe bsince we may fail to find the inner target due to it beingundefined. TheKleislicategory is a category structure which is induced by arrows into monads:Now, we can build chains of
Kleisli MaybearrowsNow the Erlang example uses a list of these “accessors” but that will fail for us because basic lists cannot store heterogenous types. It’d be okay if each of the
get_*methods were “Kleisli endomorphisms”, e.g. had the same typea ~> afor somea, but they don’t.We can make a list structure which is able to store “paths” of arbitrary Kleisli arrows, but it turns out that if all you’re interested in is the final, composed result then there isn’t a whole lot of value in doing this. You end up writing the same thing each way: what’s the difference between
foldl (.) id [a, b, c, d]anda . b . c . d, really?(Oh, it should also be stated that lenses are really closely associated with this idea. In this case, we’re talking about a generalization of lenses sometimes known as prisms, though.)
I know about the Maybe monad, that’s why I have penultimate paragraph comment :)
I haven’t read much about Kleisli
I think your composition comment at the end is stated in “A tutorial on the universality and expressiveness of fold”, but sadly Erlang won’t allow this kind of syntax
Yep, I hope I didn’t insinuate you weren’t aware of it at all. Just didn’t want to assume too much.
The idea of replacing
Cons a (Cons b (Cons c Nil)withf a (f b (f c zero)absolutely falls out of the universality offoldrfor lists. In this case you can’t quite do that sinceCons get_msg (Cons get_content Nil)is not well-typed. We can do something like this though with a “free category” constructionand now
Cons get_msg (Cons get_content (Cons get_props (Cons get_headers Nil)))is well-typed. We also have a fold principleand this one has similar universal properties in that
fold f ztransformsI just want to say that I appreciate not assuming and the nice description. I just wanted to say that I had remembered an elixir article posted about railway programming and there was a chain in the comments about why he didn’t talk about it as a maybe monad. It then turned into, “because monad is a scary word,” when in a response to a comment on the article the author said he wasn’t familiar with monads.
Just want to say that I liked the way you introduced it then gave an explanation to. Make it clear to those who didn’t know exactly what you meant (like me).
I’m really glad you appreciated it. I’ll definitely try to continue this pattern in the future! :)
If I were to do this I would not use fold directly but make a wrapper around it called something like
get_path.You mean like abstracting the whole fold thing into your get_path function, or?
Yes, so the
header3function would be something like:Unfortunately Erlang makes doing these things of things rather painful because its function syntax is so heavy.
I agree, I mean, this was mostly an example