RankNTypes: What is causing this error?

夙愿已清 提交于 2019-12-05 03:42:11

For all types a and b a function of type forall a. forall b. a -> b must be able to take a value of type a and produce a value of type b. So it must, for example, be possible to put in an Int and get out a String.

You can not get a String out of id if you put in an Int - you only ever get back the same type that you put in. So id is not of type forall a. forall b. a -> b. In fact there can be no total function of that type without typeclass constraints.


It turns out, you can sort-of-kind-of do something close(ish) to what you want using ConstraintKinds, but it's neither pretty to write nor pretty to use:

The idea is to parametrize g with constraints which specify which conditions need to be met by x, y, u and v and what the relationships between x and u and between y and v need to be. Since we don't need all those constraints in all cases, we also introduce two dummy typeclasses (one for constraints on individual parameters and one for the "relationship constraints), so that we can use those as the constraints where no constraints are needed (GHC is not able to infer constraints if we don't specify them ourselves).

Some example constraints to make this more clear:

  • If we pass in id as the function, x must be equal to u and y must be equal to v. There are no constraints on x, y, u or v individually.
  • If we pass in show, x and y must be instances of Show and u and v must be equal to String. There are no constraints on the relationship between x and u or y and v.
  • If we pass in read . show, x and y need to be instances of Show and u and v need to be instances of Read. Again no constraints on the relationships between them.
  • If we have a type class Convert a b where convert :: a -> b and we pass in convert, then we need Convert x u and Convert y v and no constraints on individual parameters.

So here's the code to implement this:

{-# LANGUAGE Rank2Types, ConstraintKinds, FlexibleInstances, MultiParamTypeClasses #-}

class Dummy a
instance Dummy a

class Dummy2 a b
instance Dummy2 a b

g :: forall c. forall d. forall e. forall x. forall y. forall u. forall v.
     (c x, c y, d u, d v, e x u, e y v) =>
     (forall a. forall b. (c a, d b, e a b) => a -> b) -> x -> y -> (u,v)
g p x y = (p x, p y)

And here's how to use it:

Using show . read to convert between different types of numbers:

> (g :: (Show x, Show y, Read u, Read v, Dummy2 x u, Dummy2 y v) => (forall a. forall b. (Show a, Read b, Dummy2 a b) => a -> b) -> x -> y -> (u,v)) (read . show) 1 2 :: (Double, Int)
(1.0,2)

Using id:

> (g :: (Dummy x, Dummy y, x~u, y~v) => (forall a. forall b. (Dummy a, Dummy b, a~b) => a -> b) -> x -> y -> (u,v)) id 1 2.0
(1,2.0)

Using show:

> (g :: (Show x, Show y, String~u, String~v, Dummy2 x u, Dummy2 x y) => (forall a. forall b. (Show a, String~b, Dummy2 a b) => a -> b) -> x -> y -> (u,v)) show 1 2.0
("1","2.0")

As you can see, this is horribly verbose and unreadable because you need to specify a signature for g every time you use it. Without this, I don't think it's possible to get GHC to correctly infer the constraints (or at least I don't know how).

When you look at a function forall a. forall b. a -> b it means it takes a value of any type and can return a value of any type. Suppose f is such a function then you can feed say f 1 to any function or f "hello" to any function because return type of f is still polymorphic while on the other hand once you give a value to id the return type is fixed (it will be of the same type as input value) and hence the error.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!