此代码正在编译:
trigger :: (Typeable n) => n () -> IO ()
trigger n = case (cast n) of
Just n' -> n'
Nothing -> error "n is not IO"
但如果我添加括号,它就不再了:
-- not compiling
trigger' :: (forall n. (Typeable n) => n ()) -> IO ()
trigger' n = case (cast n) of
Just n' -> n'
Nothing -> error "n is not IO"
它说“因使用'演员'而没有(Typeable n)的实例”。 为什么呢?
实际上我需要第二种形式,因为在我的程序中我使用的数据类型为forall
:
data Foo = Foo {a :: (Typeable n) => n ()}
trigger' :: Foo -> IO ()
trigger' (Foo n) = case (cast n) of
Just n' -> n'
Nothing -> error "n is not IO"
答案 0 :(得分:2)
trigger :: (Typeable n) => n () -> IO ()
“对于n
的任何Typeable
,我可以将n ()
变为IO ()
trigger' :: (forall n. (Typeable n) => n ()) -> IO ()
“我可以将n ()
的每个<{em> n
Typeable
的{{1}}变为IO ()
第二个没有意义;那里的 没有所需类型的值(undefined
除外)。即使有,也没有必要使用cast
;因为参数可以分配每个 n ()
类型,你可以按原样返回它;已经IO ()
作为每个n ()
的一个特例,所以你甚至不需要trigger
(除了可能会设置一个模糊的类型)。这几乎肯定不是你想要的。
如果你有一个存在主义类型,第一个签名是你需要的;如果您可以构建包含Foo
n ()
的{{1}},并且您以后希望能够运行它们如果它们恰好包含Typeable
。我怀疑这实际上是你想要的,并且你不小心错误地定义了你的数据类型,而不是IO ()
中的问题。
考虑一下:
trigger
您的{-# LANGUAGE GADTs, RankNTypes #-}
module Foo where
import Data.Typeable (Typeable, cast)
data Foo = Foo { a :: Typeable n => n () }
data Bar = Bar { bar :: forall n. Typeable n => n () }
data Baz = forall n. Typeable n => Baz { baz :: n () }
trigger :: Typeable n => n () -> IO ()
trigger n = case (cast n) of
Just n' -> n'
Nothing -> error "n is not IO"
相当于Foo
,而不是Bar
:
Baz
隐式λ :t Foo
Foo :: (forall (n :: * -> *). Typeable n => n ()) -> Foo
*Foo
λ :t Bar
Bar :: (forall (n :: * -> *). Typeable n => n ()) -> Bar
*Foo
λ :t Baz
Baz :: Typeable n => n () -> Baz
位于字段 forall
的类型签名的开头,而不是对整个构造函数进行量化。因此,您已经要求字段a
是 a
,而不是处理任何forall n. Typeable n => n ()
的构造函数以及包含某些特定但未知{{1}的字段}}
我们可以看到n ()
以n ()
的方式与您希望的方式相同:
Baz
对于trigger
,如果您可以将其字段输出,则可以直接将其用作*Foo
λ case Baz (putStrLn "yay") of Baz x -> trigger x
yay
it :: ()
而无需任何演员,但是无法将某些内容放入Foo
首先:
IO ()
因为实际上打印了“怪异”,你可以看到类型检查器接受了这个表达式;如果Foo
的假设内容可用作λ case Foo undefined of Foo x -> putStrLn "weird" >> x
weird
*** Exception: Prelude.undefined
,则完全没问题,完全不需要Foo
。但是,我已经手动摆脱了实际构建IO ()
的问题;如果不使用trigger
这样的东西,基本上是不可能的。这就是让我认为你的数据声明不正确的原因,而不是Foo
。
但是如果你的真实用例有一些东西会使包含多态字段的数据类型更合理(如果有更多约束而不是undefined
则可能可能使用) ,它也不能简单地省去trigger
并利用包含的多态性“强制转换”为更具体的类型,然后我们需要更多信息来解决您的实际用例。