简体   繁体   中英

Illegal polymorphic or qualified type in Haskell

为什么Haskell在尝试解析这种类型的签名时会抱怨?

f :: (a,s) -> (forall r.(r -> (a,r)),s)

Haskell does not support impredicative types, and in particular does not allow forall to appear under any type constructor (except -> ).

For example, Maybe (forall a. a), [forall a. a->a], ((forall a. a), Bool) Maybe (forall a. a), [forall a. a->a], ((forall a. a), Bool) are forbidden.

Use a newtype wrapper if that's what you want.

newtype T = T (forall a. a->a)
foo :: [T] -- OK
foo = [T id]

Out of curiosity, what are you trying to use this type for? It looks like the type of a concatenative combinator like quote such that [X] quote == [[X]] (sometimes called unit ). In other words, it takes a value atop the stack and wraps it in a function which, when applied, pushes that value to the stack.

Here's one representation I've used in the past for such functions. The Tupled type family converts a list of types into a nested tuple to represent a stack.

-- Tupled '[t1, t2, ..., tn] s == (t1, (t2, (... (tn, s))))
type family Tupled ts t where
  Tupled '[] t' = t'
  Tupled (t ': ts) t' = (t, Tupled ts t')

Using a newtype wrapper we can make a function (of a certain input & output arity) that's polymorphic in the “rest” of the stack.

newtype as :-> bs = Fun (forall s. Tupled as s -> Tupled bs s)

This is the standard way of hiding impredicative polymorphism , that is, using forall -quantified types under a type constructor other than a function arrow (->) , as you did when you tried to write (forall r. (r -> (a, r)), s) . Haskell doesn't support this directly, but if you use a newtype wrapper then the compiler knows exactly when to introduce and eliminate the forall .

By unwrapping this newtype and applying it to the stack type, we can apply a wrapped function to a stack.

apply :: forall z as bs. (as :-> bs) -> Tupled as z -> Tupled bs z
apply (Fun f) as = f @z as

The quote combinator wraps the top element of the stack in a function:

quote :: forall a s. (a, s) -> ([] :-> '[a], s)
quote (a, s) = (Fun $ \s' -> (a, s'), s)

unquote applies a function on the stack to the rest of the stack.

unquote
  :: forall z as bs s
  .  (Tupled as z ~ s)
  => (as :-> bs, s)
  -> Tupled bs z
unquote (f, s) = apply @z f s

(Note the equality constraint Tupled as z ~ s , which means “The input stack type s must begin with the series of types as , and whatever remains is called z ”.)

add is the addition operator (+) lifted to stacks; it just adds the top two elements of the stack.

add :: forall a. (Num a) => '[a, a] :-> '[a]
add = Fun $ \ (x, (y, s)) -> (x + y, s)

Now, quoting and unquoting an element leaves it unchanged:

unquote (quote ("hello", ()) == ("hello", ())

The addition function can be applied directly…

apply add (1, (2, ())) == (3, ())

…Or placed on the stack and then applied.

unquote (add, (1, (2, ()))) == (3, ())

This requires the following extensions:

  • DataKinds to allow type-level lists of types

  • RankNTypes and ScopedTypeVariables to allow explicit forall s and bring type variables into scope so we can refer to them with TypeApplications , because we need AllowAmbiguousTypes to defer specifying the “stack” type until a call site, as in apply @zf as

  • TypeFamilies to enable the Tupled type family

  • TypeOperators to give the nice symbolic name :-> to wrapped function types

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM