The recently released Seqes
library offers functors to mix the Stdlib.Seq
abstraction with monads. The original impetus for development was the use of Lwt, result and Lwt+result monads in the octez code-base. The use of these monads in combination with Seq
introduced a lot of boilerplate code, which Seqes
aims to alleviate.
Code duplication in Seqes
The Lwt “monad” (something something exceptions can break one of the monadic law) has one parameter. Thus the functor allowing to define a Seq+Lwt module is:
module Make1
(Mon: sig
type 'a t
val return : 'a -> 'a t
val bind : 'a t -> ('a -> 'b t) -> 'b t
end)
: S1
with type 'a mon := 'a Mon.t
= struct
(* code here *)
end
The Result monad has two parameters. Thus the functor for defining a Seq+Result module is:
module Make2
(Mon: sig
type ('a, 'e) t
val return : 'a -> ('a, 'e) t
val bind : ('a. 'e) t -> ('a -> ('b, 'e) t) -> ('b, 'e) t
end)
: S2
with type ('a, 'e) mon := ('a, 'e) Mon.t
= struct
(* code here *)
end
Interestingly, there is no difference in the code of the two functor apart from type definition.
Technically, it’s worse than just that:
- There is a (duplicated) functor for making traversors over the
Stdlib.Seq.t
. - There is a (duplicated) functor as presented above.
- There is a (duplicated) functor inside the functor presented above formaking additional traversors.
But the observation is still valid: only the types differ, the control structures are 1-to-1 identical. The library is essentially fully duplicated: there’s a one-parameter version and two-parameter version.
Can I encode the arity of a type constructor in the type system and use that to deduplicate the code?
Of course once can pass a two-parameter monad as a one-parameter monad. The two-parameter functor will accept the following monad:
type ('a, 'e) t = 'a Lwt.t
let return = Lwt.return
let bind = Lwt.bind
But this returns a two-parameter Seq-like type.
I notice that the kinded
and higher_kinded
libraries have a similar approach of duplicating the code for each arity of the type constructor. And I think that if there was a known trick it would be used in those libraries.
Anyone has examples? Ideas?
Alternatively, explanations as to why it’s not possible?