你可以部分限制Haskell中的类型吗?
是否可以提供类型签名给Haskell值,其中包含用于填充类型推断算法的“空白”?
上下文极其人为的例子:
m = return ('I', (("don't", "really"), "care", ["what", "this"], "type"), "is")
b = isJust m
这工作。 使用isJust m
in b
将m
的类型约束为Maybe <something>
,并且m
的定义将m
的类型约束为<something> (Char, ((String, String), String, [String], String), String)
,并且编译器将这两条信息放在一起以确定m
的精确类型。
但是,说我没有向m
应用任何Maybe
特定的函数,所以我需要一个手动类型签名来停止return
是多态的。 我不能这样说:
m :: Maybe a
m = return ('I', (("don't", "really"), "care", ["what", "this"], "type"), "is")
因为这是不正确的。 该类型不是Maybe a
所有的a
,它Maybe a
a
我想编译器推断; 程序中有足够的信息可以让编译器做到这一点,从我的第一个例子中可以看出,编译器能够将类型的多个约束放在一起,单独的约束不足以确定类型是什么,但他们一起完全指定类型。
我想要的是能够给出像m :: Maybe _
这样的类型,其中_
意思是“你知道这里发生了什么”,而不是像m :: Maybe a
“这是一个刚性类型变量”。
有什么方式可以说这样的事吗? 我能看到的选择是明确地给出完整的类型:
m :: Maybe (Char, ((String, String), String, [String], String), String)
m = return ('I', (("don't", "really"), "care", ["what", "this"], "type"), "is")
或者对表达式的一部分赋予类型签名,这样可以限制类型签名的Maybe
部分,但不是a
,例如:
m = (return :: a -> Maybe a) ('I', (("don't", "really"), "care", ["what", "this"], "type"), "is")
或者在没有显式类型签名的情况下离开m
,并引入约束m
未使用的额外定义:
m = return ('I', (("don't", "really"), "care", ["what", "this"], "type"), "is")
b = isJust m
或者直接使用单形函数:
m = Just ('I', (("don't", "really"), "care", ["what", "this"], "type"), "is")
显然,这不是特定于Maybe
,也不是* -> *
类型构造函数的参数; 我可以想象,如果不想说“这个值是一个monadic Int
对于任何monad”,或者“这是一个从Int
到某种其他类型的函数”,而不是说“这个值是一个单值Int
,对于某个monad”这是从Int
到任何其他类型的函数。
我最感兴趣的是是否有一些东西允许我公平地直接声明类似于上面关于值的声明,以便于阅读,而不是难以阅读变通方法(例如为return
提供显式类型签名)。 我知道如果我的目标是简单地向编译器中获取额外的输入信息来关闭模糊类型变量,那么有无数的方法可以做到这一点。
GHC不允许你直接指定部分签名,不幸的是,尽管这样做会很棒。
在这种情况下做你想做的一种方法是m = return ... `asTypeOf` (undefined :: Maybe a)
,其中asTypeOf
是一个Prelude函数:: a -> a -> a
返回它的第一个参数但是强制它与第二个统一。
在你的评论中这是一个很好的观点 - undefined :: SomeType
让我很难过。 这是另一个解决方案:
import Data.Proxy
proxiedAs :: a -> Proxy a -> a
proxiedAs = const
现在你可以说m = return ... `proxiedAs` (Proxy :: Proxy (Maybe a))
,并且没有⊥s在视线内。
你可以写下如下的东西:
asMaybe :: Maybe a -> Maybe a
asMaybe = id
m = asMaybe $ return ('I', (("don't", "really"), "care", ["what", "this"], "type"), "is")
我在classy-prelude中使用这种技巧,提供asByteString
, asSet
, asList
等。
使用GHC 7.10现在有了PartialTypeSignatures扩展,它使我在我的问题中编写的假想的下划线语法成为可能。 上面链接的文档页面中的示例:
not' :: Bool -> _
not' x = not x
-- Inferred: Bool -> Bool
maybools :: _
maybools = Just [True]
-- Inferred: Maybe [Bool]
just1 :: _ Int
just1 = Just 1
-- Inferred: Maybe Int
filterInt :: _ -> _ -> [Int]
filterInt = filter -- has type forall a. (a -> Bool) -> [a] -> [a]
-- Inferred: (Int -> Bool) -> [Int] -> [Int]
仅使用PartialTypeSignatures
扩展,编译器会使用推断的类型发出警告,以防它们只是您打算在最终版本之前填写的占位符。 通过添加-fno-warn-partial-type-signatures
标志,您可以告诉它您打算将部分签名保留。