
[英]Can I constrain the parametric polymorphic type on type/data constructor in Haskell?
[英]Can you partially constrain a type in Haskell?
是否可以为包含“空白”的Haskell值提供类型签名以填充类型推断算法?
背景的极为人为的例子:
m = return ('I', (("don't", "really"), "care", ["what", "this"], "type"), "is")
b = isJust m
这有效。 使用isJust m
在b
约束的类型m
是Maybe <something>
,和m
的定义约束的类型m
为<something> (Char, ((String, String), String, [String], String), String)
,并且编译器将这两条信息放在一起以计算m
的精确类型。
但是我说我没有将任何Maybe
特定函数应用于m
,所以我需要一个手动类型签名来阻止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”而不想说“这个值是任何 monad的monadic Int
”,或者“这是从Int
到其他类型的函数”而不说“这是从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扩展,它完全支持我在我的问题中写的假设下划线语法。 上面链接的doc页面中的示例:
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
标志,您可以告诉它您打算将签名保留为部分。
从Michael Snoyman的建议中综合一点,如果我:
我可以这样做:
type Assert a = a -> a
m = (id :: Assert (Maybe a)) $ return ('I', (("don't", "really"), "care", ["what", "this"], "type"), "is")
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.