I've got a function that's doing something currently working with particular datatypes. I was wondering if I could make it a general. Here's a generalised version of it's signature:
f :: Monad m => ((a -> b) -> c -> d) -> (a -> m b) -> m c -> m d
If the above can't be written, perhaps the more restricted version can?
f2 :: Monad m => ((a -> a) -> b -> b) -> (a -> m a) -> m b -> m b
No, it is impossible, at least without non-termination or unsafe operations.
The argument is essentially similar to this one: we exploit f to inhabit a type which we know can't be inhabited.
Assume there exists
f :: Monad m => ((a -> b) -> c -> d) -> (a -> m b) -> m c -> m d
Specialize c ~ ()
f :: Monad m => ((a -> b) -> () -> d) -> (a -> m b) -> m () -> m d
Hence
(\g h -> f (\x _ -> g x) h (return ()))
:: Monad m => ((a -> b) -> d) -> (a -> m b) -> m d
Speciazlize d ~ a.
(\g h -> f (\x _ -> g x) h (return ()))
:: Monad m => ((a -> b) -> a) -> (a -> m b) -> m a
Speclialize m ~ Cont t
(\g h -> runCont $ f (\x _ -> g x) (cont . h) (return ()))
:: ((a1 -> b) -> a) -> (a1 -> (b -> r) -> r) -> (a -> r) -> r
Take h = const
(\g -> runCont $ f (\x _ -> g x) (cont . const) (return ()))
:: ((r -> b) -> a) -> (a -> r) -> r
Hence
(\g -> runCont (f (\x _ -> g x) (cont . const) (return ())) id)
:: ((r -> b) -> r) -> r
So, the type ((r -> b) -> r) -> r is inhabited, hence by the Curry-Howard isomoprhism it corresponds to a theorem of propositional intuitionistic logic. However, the formula ((A -> B) -> A) -> A is Peirce's law which is known to be non provable in such logic.
We obtain a contradiction, hence there is no such f.
By contrast, the type
f2 :: Monad m => ((a -> a) -> b -> b) -> (a -> m a) -> m b -> m b
is inhabited by the term
f2 = \ g h x -> x
but I suspect this is not what you really want.
There's a problem. Knowing c doesn't give you any information about which as will be passed to (a -> b). You either need to be able to enumerate the universe of as or be able to inspect the provided a arguments with something like
(forall f. Functor f => ((a -> f b) -> c -> f d)
In which case it becomes almost trivial to implement f.
Instead of trying to implement f in general, you should try to generalize your functions like ((a -> b) -> c -> d) to see if you can replace them with lenses, traversals, or something similar.
Related
I have a lens(')
myLens' :: (Functor f) => (a -> f a) -> (s -> f s)
And I need a function
lensWithoutFunctor' :: ((a -> b) -> (f a -> f b)) -> (a -> f a) -> (s -> f s)
(The particular type I'm interested in is
type PopType x a = x -> Maybe (a, x)
which could be made into a Functor if I wanted)
Is there a better way to do this other than create a Functor-implementing newtype and then doing something involving "ala"?
Does such a thing exist in Haskell's Prelude?
wfmap :: Functor f
=> a
-> (a -> b)
-> (b -> a)
-> (b -> f b)
-> f a
wfmap x u w g = fmap (w) (g (u x))
In a project I'm working on, I often found myself 'converting' a type to another, process it and 'converting' it back.
Reordering the arguments, as leftaroundabout suggests, allows for a tidier definition:
wfmap :: Functor f => (a -> b) -> (b -> a) -> (b -> f b) -> a -> f a
wfmap u w g = fmap w . g . u
As for library support, lens provides nifty support for isomorphisms. A bit more broadly, as Gurkenglas notes...
Functor f => (b -> f b) -> a -> f a is also called Lens' a b and is the centerpiece of the lens library.
Without diving into the details of how and why that works, one consequence is that your function might be defined as:
wfmap :: Functor f => (a -> b) -> (b -> a) -> (b -> f b) -> a -> f a
wfmap u w g = (iso u w) g
Or even:
wfmap :: Functor f => (a -> b) -> (b -> a) -> (b -> f b) -> a -> f a
wfmap = iso
wfmap is just (a specialised version of) iso, which gives out a function which can be used to convert an b -> f b function on the isomorphism "destination" to an a -> f a one on the isomorphism "source".
It is also worth mentioning mapping, which can be used for the somewhat different purpose of applying fmap on the other side of an isomorphism:
GHCi> :t \u w g -> over (mapping (iso u w)) (fmap g)
\u w g -> over (mapping (iso u w)) (fmap g)
:: Functor f => (s -> a) -> (b -> t) -> (a -> b) -> f s -> f t
GHCi> :t \u w g -> under (mapping (iso u w)) (fmap g)
\u w g -> under (mapping (iso u w)) (fmap g)
:: Functor f => (s -> a) -> (b -> a1) -> (a1 -> s) -> f b -> f a
Finally, note that iso u w can be replaced by any Iso you might find in the libraries or have predefined elsewhere.
I am attempting to implement a simply typed lambda calculus type checker. When running sanity tests I tried typing (S K K) and my type checker throws this error:
TypeMismatch {firstType = t -> t, secondType = t -> t -> t}
The offending term is clearly the (S K K)
(\x:t -> t -> t.\y:t -> t.\z:t.x z (y z)) (\x:t.\y:t.x) (\\x:t.\y:t.x)
I think the problem arises from a lack of polymorphism because when I type check this haskell code it works fine:
k x y = x
s x y z = x z (y z)
test = s k k -- type checks
but if I specialize the type:
k :: () -> () -> ()
k x y = x
s :: (() -> () -> ()) -> (() -> ()) -> () -> ()
s x y z = x z (y z)
test = s k k -- doesn't type check
Just for reference my type system is a simple as it gets:
data Type = T | TArr Type Type
full source
I will steal the ideas from a previous answer of mine to show how to ask ghci your question. But first I am going to reformulate your question slightly.
In Haskell, we have
s :: (a -> b -> c) -> (a -> b) -> (a -> c)
k :: a -> b -> a
and the question we want to ask is "What do these types look like after type-checking s k k?". More to the point, if we rewrite them with distinct unification variables,
s :: (a -> b -> c) -> (a -> b) -> (a -> c)
k :: d -> e -> d
k :: f -> g -> f
s k k :: h
then the question becomes a unification one: we are trying to unify the type of s with the type it's being used at -- namely (d -> e -> d) -> (f -> g -> f) -> h. Now that we have a unification question in hand, we can ask in the format showed in my other answer:
> :{
| :t undefined
| :: ((a -> b -> c) -> (a -> b) -> (a -> c))
| ~ ((d -> e -> d) -> (f -> g -> f) -> h)
| => (a, b, c, d, e, f, g, h)
| :}
undefined
:: ((a -> b -> c) -> (a -> b) -> (a -> c))
~ ((d -> e -> d) -> (f -> g -> f) -> h)
=> (a, b, c, d, e, f, g, h)
:: (f, g -> f, f, f, g -> f, f, g, f -> f)
And now we can see why your version doesn't work: in your version, you've instantiated all polymorphic variables to the base type T; but since b ~ g -> f, e ~ g -> f, and h ~ f -> f are manifestly arrow types, that certainly isn't going to work! However, any choices for f and g will work if we respect the substitution above; in particular if we choose f ~ T and g ~ T, then we have
s :: (T -> (T -> T) -> T) -> (T -> (T -> T)) -> (T -> T)
k1 :: T -> (T -> T) -> T
k2 :: T -> T -> T
s k1 k2 :: T -> T
How to calculate type of (.)(.) in Haskell?
I know that it should be
(.)(.) :: (a -> b -> c) -> a -> (a1 -> b) -> a1 -> c
But how to calculate it without computer?
(.) :: (b -> c ) -> ((a -> b) -> (a -> c))
(.) :: ((e -> f) -> ((d -> e) -> (d -> f)))
(.)(.) :: ((a -> (e -> f)) -> (a -> ((d -> e) -> (d -> f))))
(.)(.) :: (a -> (e -> f)) -> (a -> ((d -> e) -> (d -> f)))
(.)(.) :: (a -> e -> f) -> a -> ((d -> e) -> (d -> f))
(.)(.) :: (a -> e -> f) -> a -> (d -> e) -> (d -> f)
(.)(.) :: (a -> e -> f) -> a -> (d -> e) -> d -> f
by (manual) pattern-matching and rewriting types-variables
(.) has type (b -> c) -> ((a -> b) -> a -> c) so the first argument should have type b -> c.
Now if we use it again we have to substitute b with b' -> c' and c with (a' -> b') -> a' -> c') (the second (.) should have type (b' -> c') -> ((a' -> b') -> a' -> c')) and we get
(a -> b' -> c') -> a -> (a' -> b') -> a' -> c'
which is (after renaming) the same as above.
Note that I used a -> b -> c = a -> (b -> c) here
using GHCi
yeah I know - you want it by hand - but GHCi is such a valuable tool that you really should use it to confirm your manual labor.
Here from a terminal:
$ ghci
GHCi, version 7.10.1: http://www.haskell.org/ghc/ :? for help
Prelude> :t (.)(.)
(.)(.) :: (a -> b -> c) -> a -> (a1 -> b) -> a1 -> c
Prelude>
as you can see the type is (a -> b -> c) -> a -> (a1 -> b) -> a1 -> c
btw: :t is short for :type and you can see all commands with :help from inside a GHCi session.
Since I wasn't particularly satisfied with the missing explanations in the accepted answer, I give my POV as well:
-- this is the original type signature
(.) :: (b -> c) -> (a -> b) -> a -> c
-- now because of haskell polymorphism,
-- even 'b' and 'c' and so on could be functions
--
-- (.)(.) means we shove the second function composition
-- into the first as an argument.
-- Let's give the second function a distinct type signature, so we
-- don't mix up the types:
(.) :: (e -> f) -> (d -> e) -> d -> f
-- Since the first argument of the initial (.) is of type (b -> c)
-- we could say the following if we apply the second (.) to it:
(b -> c) == (e -> f) -> (d -> e) -> d -> f
-- further, because of how currying works, as in
(e -> f) -> (d -> e) -> d -> f == (e -> f) -> ((d -> e) -> d -> f)
-- we can conclude
b == (e -> f)
c == (d -> e) -> d -> f
-- since we passed one argument in, the function arity changes,
-- so we'd actually only have (a -> b) -> a -> c left, but that
-- doesn't represent the types we have now, so we have to substitute
-- for b and c, so
(a -> b) -> a -> c
-- becomes
(.)(.) :: (a -> (e -> f)) -> a -> (d -> e) -> d -> f
-- and again because of currying we can also write
(.)(.) :: (a -> e -> f) -> a -> (d -> e) -> d -> f
id :: a -> a
liftM2 :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c
ap :: (Monad m) => m (a -> b) -> m a -> m b
ap = liftM2 id
Could you help explain how the type of ap is inferred when liftM2 is applied to id? Moreover, is it valid to ask the equivalent question but more specifically how (a -> b -> c) -> m a is reduced to m (a -> b) in this case?
Lets try to find out what liftM2 id's type should be. First of all, we change the type parameter in id, so that we can fix this a little bit easier.
id :: x -> x
liftM2 :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c
Next, we add additional parentheses in liftM2 and remember that a -> b -> c is actually a -> (b -> c):
id :: x -> x
liftM2 :: (Monad m) => (a -> (b -> c)) -> m a -> (m b -> m c)
Now we shift x -> x to line it up with other types in liftM2:
id :: x -> x
liftM2 :: (Monad m) => (a -> (b -> c)) -> m a -> (m b -> m c)
Ok. This tells us that a ~ (b -> c) in liftM2 id, or:
id :: (b -> c) -> (b -> c)
liftM2 :: (Monad m) => ((b -> c) -> (b -> c))
-> m (b -> c) -> (m b -> m c)
Now we can use those specialized versions:
liftM2 id :: Monad m => m (b -> c) -> (m b -> m c)
We drop the superfluous parentheses and end up with the correct type:
liftM2 id :: Monad m => m (b -> c) -> m b -> m c
id has the type a -> a. The first question is how can we unify a -> a with the type of liftM2's argument, (a -> b -> c)? The trick is to replace the a in a -> a with (a -> b) giving us (a -> b) -> (a -> b) or, equivalently, (a -> b) -> a -> b. (As a neat side note, this is the type of $, which means $ is just id with a restricted type!)
Now we combine (a -> b) -> a -> b with liftM2's whole type:
Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
We replace a1 with a -> b, a2 with a and r with b giving us:
Monad m => ((a -> b) -> a -> b) -> m (a -> b) -> m a -> m b
Finally, once we apply liftM2 to id, the result has the same type minus the first argument:
liftM2 id :: Monad m => m (a -> b) -> m a -> m b
And there we are: the type of ap.
A good intuition for this is based on my earlier observation about $. $ is the normal function application operator; ap is function application lifted over a monad. It makes sense that liftM2 ($) gives you ap because that's what ap fundamentally means... and id is just a version of $ with a more general type.