Note: This is a repost of another question, which the author deleted. Here's the original question:
I have this polyvariadic comp
function in Javascript and was wondering if a similar implementation in Haskell were possible. I am mostly interested in comp
's type:
const comp = f => Object.assign(
g => comp([g].concat(f)),
{run: x => f.reduce((acc, h) => h(acc), x)}
);
const inc = n => n + 1;
const sqr = n => n * n;
const repeatStr = s => n => Array(n + 1).join(s);
comp(repeatStr("*")) (inc) (sqr).run(2); // "*****"
comp(repeatStr("*"))
(inc)
(inc)
(inc)
(inc)
(inc).run(0); // "*****"
comp
builds up a heterogeneous array that usually doesn't have a type in Haskell. I guess such a variadic function must be polymorphic in its return type. However, this task exceeds my Haskell knowledge by far. Any clue would be helpful.
Context
I use a Javascript runtime type checker so that I can build up the array inside comp
in a type-safe manner. It requires explicit type annotations and supports only parametric and rank-2 polymorphism.
You're right. You can't build a heterogeneous list of composable functions in Haskell(1). However, you can create your own list data type for composable functions as follows:
The
Id
constructor is similar to[]
and theComp
constructor is similar to:
but with the arguments flipped.Next, we use the varargs pattern to create a polyvariadic function. To do so, we define a type class:
Note that our state is
Comp b c
and our result is eitherComp b c
or a function that takes another function(a -> b)
as an input and composes it with our state to produce a newChain
calledr
with stateComp a c
. Let's define instances for these now:The
comp
function can now be defined aschain Id
(i.e. the chain with the empty listId
as its state). We can finally use thiscomp
function as we'd do in JavaScript:Putting it all together:
As you can see, the type of
comp
isChain b b c => c
. To define theChain
type class we requireMultiParamTypeClasses
andFunctionalDependencies
. To use it we requireFlexibleInstances
. Hence, you'll need a sophisticated JavaScript runtime type checker in order to correctly type checkcomp
.Edit: As naomik and Daniel Wagner pointed out in the comments, you can use an actual function instead of a list of composable functions as your internal representation for the state of
comp
. For example, in JavaScript:Similarly, in Haskell:
Note that even though we don't use GADTs anymore we still require the
GADTs
language extension in order to use the equality constraintc ~ c'
in the first instance ofChain
. Also, as you can seerun g . f
has been moved from the definition ofrun
into the second instance ofChain
. Similarly,id
has been moved from the definition ofrun
into the definition ofcomp
.(1) You can use existential types to create a list of heterogeneous functions in Haskell but they won't have the additional constraint of being composable.