3

在 Haskell 中研究多变量函数时,我偶然发现了以下 SO 问题:

如何创建多变量haskell函数?

Haskell,多变量函数和类型推断

并认为我会通过实现一个函数来尝试它,该函数接受可变数量的字符串并将它们连接/合并成一个字符串:

{-# LANGUAGE FlexibleInstances #-}

class MergeStrings r where
    merge :: String -> r
instance MergeStrings String where
    merge = id
instance (MergeStrings r) => MergeStrings (String -> r) where
    merge acc = merge . (acc ++)

如果我用至少一个字符串参数调用合并并且我提供最终类型,这到目前为止有效。

foo :: String
foo = merge "a" "b" "c"

省略最终类型会导致错误,即编译如下

bar = merge "a" "b" "c"

结果是

test.hs:12:7: error:
    • Ambiguous type variable ‘t0’ arising from a use of ‘merge’
      prevents the constraint ‘(MergeStrings t0)’ from being solved.
      Relevant bindings include bar :: t0 (bound at test.hs:12:1)
      Probable fix: use a type annotation to specify what ‘t0’ should be.
      These potential instances exist:
        instance MergeStrings r => MergeStrings (String -> r)
          -- Defined at test.hs:6:10
        instance MergeStrings String -- Defined at test.hs:4:10
    • In the expression: merge "a" "b" "c"
      In an equation for ‘bar’: bar = merge "a" "b" "c"
   |
12 | bar = merge "a" "b" "c"
   |

错误消息非常有意义,因为我可以很容易地想出,例如

bar :: String -> String
bar = merge "a" "b" "c"

baz = bar "d"

不是渲染bar成单个字符串,而是渲染成一个接受并返回一个字符串的函数。

有没有办法告诉 Haskell 结果类型必须是 type String?例如,在没有明确定义的情况下Text.Printf.printf "hello world"计算类型。String

4

2 回答 2

3

printf由于GHCi中的类型默认,无需类型注释即可工作。允许您在show $ 1 + 2不指定具体类型的情况下进行评估的相同机制。

GHCi 尝试评估 type 的表达式IO a,因此您只需要添加适当的实例MergeStrings

instance (a ~ ()) => MergeStrings (IO a) where
    merge = putStrLn
于 2020-05-31T12:17:51.130 回答
2

布拉德(在评论中)和马克斯说违约并没有错printf "…" …</code> to IO ( ) is the reason for it working in ghci without type annotations. But it is not the end of the story. There are things we can do to make your definition of bar work.

First, I should mention the «monomorphism restriction» — an obscure and unintuitive type inference rule we have in Haskell. For whatever reason, the designers of Haskell decided that a top level definition without a type signature should have no polymorphic variables in its inferred type — that is, be monomorphic. <code>bar是多态的,所以你可以看到它会受到影响。

一些类型类(特别是数字)默认规则,允许你说x = 13没有类型签名并让它推断出x :: Integer——或者你设置为默认的任何其他类型。类型默认仅适用于少数祝福类,因此您不能为自己的类使用它,并且没有指定的默认 GHC 无法决定选择哪种特定的单态类型。

但是你可以做其他事情,除了默认,让类型检查器满意——要么:

Nowbar是多态的,可以按您的预期工作。看:

λ putStrLn bar
abc
λ putStrLn (bar "x")
abcx
λ putStrLn (bar "x" "y")
abcxy

您还可以使用默认值来制作诸如show bar工作之类的表达式。由于是启用扩展默认规则Show时可以默认的类之一,因此您可以在要使用的模块中发出,它会按预期工作。default (String)show bar

于 2020-05-31T13:14:52.260 回答