I take it by "weird" you pretty much mean higher order functions. This expression contains two: map :: (a -> b) -> [a] -> [b]
and (.) :: (b -> c) -> (a -> b) -> a -> c
. It is also a lambda, so likely a higher order function itself. Each parenthesized arrow here is the type of a function parameter.
map
reveals that y
must return a list of items that x
accepts as an argument. So they have the partial signatures x :: _yitem -> _outeritem
and y :: _yarg -> [_yitem]
, where the return value of this map
is of type [_outeritem]
. Note that we don't know yet how many arrows fit in these wildcards.
(. x)
translates to \l -> l . x
which translates to \l r -> l (x r)
. This entire lambda is an argument that fits y
, so y
is a higher order function. l
must accept the return value from x
. That has a name, so l :: _outeritem -> _lret
, and (. x) :: (_outeritem -> _lret) -> _xarg -> _lret
, since r
is used as the argument for x
. Oh, and _xarg
is known because of the map to be _yitem
.
Okay, that was a bunch of confusing steps in their own right, so let's line up the results:
type OuterLambda = _xtype -> _ytype -> MapRet
x :: _yitem -> _outeritem
type MapRet = [_outeritem]
y :: YArg -> [_yitem]
type YArg = (_outeritem -> _lret) -> _yitem -> _lret
y :: ((_outeritem -> _lret) -> _yitem -> _lret) -> [_yitem]
Progress! This has names for every type to and from x
and y
. But our expression is a lambda, so we must accept those two:
(_yitem -> _outeritem) ->
(((_outeritem -> _lret) -> _yitem -> _lret) -> [_yitem]) ->
[_outeritem]
That's one very long type. Let's compare it to the compiler inferred type that Yuji Yamamoto showed us:
(a0 -> b0) ->
(((b0 -> c0) -> a0 -> c0) -> [a0]) ->
[b0]
It matches. We have here quite a few orders of function: the expression expects functions x
and y
, and y
expects a function that itself takes a l
function. And all of the types we do have names for may in turn be arbitrarily complex.