【问题标题】:Haskell Arrows inside Tuples元组内的 Haskell 箭头
【发布时间】:2012-02-27 13:33:00
【问题描述】:

我想创建一个元组,它包含一个箭头和一个描述箭头的字符串。如果我使用函数(而不是箭头)这样做,则以下工作如预期:

funTimes10 = (*10)
describe10 = "times 10"

tuple10 :: (Num b) => ((b -> b), String)
tuple10 = (,) funTimes10 describe10

我可以使用fst 访问该函数,使用snd 我可以获取该函数的描述字符串。

但是,如果我用箭头交换函数,如下所示:

aTuple10 :: (Arrow a, Num b) => (a b b, String)
aTuple10 = (,) (arr funTimes10) describe10
  • fst 仍然有效并返回我的箭头,但是
  • 我没有得到任何带有snd 的描述字符串。

我只收到这个错误信息:

Ambiguous type variable `a0' in the constraint:
  (Arrow a0) arising from a use of `aTuple10'
Probable fix: add a type signature that fixes these type variable(s)
In the first argument of `snd', namely `aTuple10'
In the expression: (snd aTuple10)
In an equation for `it': it = (snd aTuple10)

为什么我会收到这个错误,我应该怎么做才能避免它?

【问题讨论】:

    标签: haskell functional-programming tuples arrows


    【解决方案1】:

    我们来看看snd的类型:

    snd :: (foo, x) -> x
    

    (为了清楚起见,我重命名了类型变量)

    类型声明的是,对于类型为foox 的元组,返回x 类型的内容。这里要知道的重要一点是,价值体系又名。 Haskell 中的 runtime 是惰性的,Haskell 的类型系统是严格的,这意味着必须知道 foox 的类型,然后才能调用 snd

    在第一种情况下,当你只有一个Num b => (b -> b, String) 时,调用snd 将使b 模棱两可,因为你没有在任何地方提及它的具体类型,也无法从返回类型中推断出来因为foo ~ bx 不同。换句话说:因为(b, b) 可以是any 数字类型的元组,而类型检查器无法确定是哪一个,所以它是模棱两可的。这里的诀窍是我们将启动 Haskell 的默认规则,该规则规定如果数字类型不明确,它应该默认为 Integer。如果您使用-Wall 打开警告,它会说这正在发生。所以,我们的类型变成了(Integer -> Integer, String),并且可以调用snd

    然而,在第二种情况下,我们仍然设法通过默认规则推断b,但a 没有默认Arrow,所以我们被卡住了!您必须明确指定您想要的箭头才能继续!您可以先在其他地方使用 aTuple10 的值来做到这一点:

    let bla = aTuple10  -- We do this because `aTuple10` can have type variables, but `bla` cannot (by default)
    fst bla (23 :: Int) -- This fixes the type of `bla`, so that `a ~ (->)` and `b ~ Int`
    print $ snd bla     -- So the arrow isn't ambiguous here
    

    ...或者你可以指定你想要的类型:

    print $ snd (aTuple10 :: (Int -> Int, String))
    

    PS如果您想更改模糊数字的默认类型,default keyword 可以帮助您。

    【讨论】:

    • 多么令人讨厌。人们会期望类型系统能够得出snd aTuple10 具有类型String 的结论;这可以被认为是实现中的错误吗? Haskell 2010 肯定没有指定这种行为。有人可能会争辩说,如果实现不知道第一件事有什么类型,它就不会知道第二件事在内存中的位置,但是由于我们在这里处理盒装元组,所以应该总是有两个指针,因此无论第一个元素的类型如何,第二个元素都很容易定位。
    • class Boolish a where toBool :: a -> Bool; foo :: Boolish a => (a, b) -> b; foo (a, b) = if toBool a then b else undefined 这样的函数是可以想象的,所以函数的结果可以依赖于一个模棱两可的参数。在这种情况下,snd 的特殊情况会很奇怪。
    • 你拯救了我的一天,(23 :: Int) 的把戏让魔术变得神奇!谢谢
    【解决方案2】:

    我试图编译这个:

    import Control.Arrow
    
    funTimes10 = (*10)
    describe10 = "times 10"
    
    tuple10 :: (Num b) => ((b -> b), String)
    tuple10 = (,) funTimes10 describe10
    
    aTuple10 :: (Arrow a, Num b) => (a b b, String)
    aTuple10 = (,) (arr funTimes10) describe10
    

    但我明白了:

    Could not deduce (b ~ Integer)
    from the context (Arrow a, Num b)
      bound by the type signature for
                 aTuple10 :: (Arrow a, Num b) => (a b b, String)
      at D:\dev\haskell\arr_tuple.hs:10:1-42
      `b' is a rigid type variable bound by
          the type signature for
            aTuple10 :: (Arrow a, Num b) => (a b b, String)
          at D:\dev\haskell\arr_tuple.hs:10:1
    Expected type: b -> b
      Actual type: Integer -> Integer
    In the first argument of `arr', namely `funTimes10'
    In the first argument of `(,)', namely `(arr funTimes10)'
    

    所以,我的猜测是您需要决定要使用哪个箭头实例。 IE。您可能需要使用注解指定 arr funTimes 的具体类型。

    【讨论】:

    • 这里的错误与手头的问题完全无关。出现是因为您忘记了funTimes10 的类型签名。查看Monomorphism Restriction
    • @dflemstr 我的回答是否显示原始问题有问题? 没有忘记类型签名,原来的帖子中没有。虽然我没有描述潜在的问题(因为我没有看到它),但我认为不值得对我的答案投反对票。
    • 我认为 OP 只是复制了他的函数的定义来演示他正在使用哪些类型的值。他有一个多态的funTimes10,否则他会得到一个不同的错误。
    猜你喜欢
    • 2011-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-04
    • 1970-01-01
    • 2017-10-06
    相关资源
    最近更新 更多