你能部分约束Haskell中的类型吗?

时间:2012-12-07 06:12:05

标签: haskell type-inference

是否可以为包含“空白”的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 _的类型,其中_表示“你弄清楚这里的内容”,而不是“这是一个严格的类型变量”,如{ {1}}。

有没有办法说出这样的话?我能看到的替代方案是明确给出完整类型:

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 * -> *,对于某些monad”而不想说“这个值是任何 monad的monadic Int”,或者“这是从Int到其他类型的函数”,不说“这是从Int任何其他类型`的函数。

我最感兴趣的是,是否存在允许我公平地直接声明类似上述值的声明,以便于阅读,而不是难以阅读的解决方法(比如为{{1}提供显式类型签名})。我知道如果我的目标是简单地将额外的输入信息输入编译器以关闭模糊类型变量,那么有无数种方法可以做到这一点。

4 个答案:

答案 0 :(得分:6)

不幸的是,GHC并不允许您直接指定部分签名,但这样做会很棒。

在这种情况下,您需要执行的操作的一种方法是m = return ... `asTypeOf` (undefined :: Maybe a),其中asTypeOf是Prelude函数:: a -> a -> a,它返回其第一个参数但强制它与第二个参数统一。< / p>


这是您在您的评论说的很对 - undefined :: SomeType让我伤心了。这是另一个解决方案:

import Data.Proxy

proxiedAs :: a -> Proxy a -> a
proxiedAs = const

现在你可以说m = return ... `proxiedAs` (Proxy :: Proxy (Maybe a)),而且看不到任何人。

答案 1 :(得分:5)

你可以这样写:

asMaybe :: Maybe a -> Maybe a
asMaybe = id

m = asMaybe $ return ('I', (("don't", "really"), "care", ["what", "this"], "type"), "is")

我在classy-prelude中使用这种技巧,提供asByteStringasSetasList等。

答案 2 :(得分:3)

使用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标志后,您可以告诉它您打算将签名保留为部分。

答案 3 :(得分:1)

如果我:

,从Michael Snoyman的建议中综合一点
  1. 不想为我想要的每个类型断言定义一个特定的函数
  2. 希望实际上将类型写成与我正在制作关于
  3. 的类型断言的类型
  4. 不想写两次类型
  5. 我可以这样做:

    type Assert a = a -> a
    
    m = (id :: Assert (Maybe a)) $ return ('I', (("don't", "really"), "care", ["what", "this"], "type"), "is")