首页 文章

在haskell中键入带有列表的函数的签名

提问于
浏览
3

我刚开始学习Haskell并且正在阅读“Learnyouahaskell”一书 . 我遇到过这个例子

tell :: (Show a) => [a] -> String  
tell [] = "The list is empty"

我知道 (Show a) 这里是一个类约束和参数类型,在这种情况下 a 必须能够"showable" .

考虑到 a 这里是列表而不是列表的元素,为什么我不能声明这样的函数: -

tell :: (Show a) =>a->String

编辑1: - 从下面的答案我似乎明白,需要为模式匹配指定 a 的具体类型 . 考虑到这一点,下面的正确实施是什么: -

pm :: (Show a) =>a->String
pm 'g'="wow"

它给我的错误如下

Could not deduce (a ~ Char)
from the context (Show a)
  bound by the type signature for pm :: Show a => a -> String
  at facto.hs:31:7-26
  `a' is a rigid type variable bound by
      the type signature for pm :: Show a => a -> String at facto.hs:31:7
In the pattern: 'g'
In an equation for `pm': pm 'g' = "wow"

失败,模块加载:无 .

我从错误消息中了解到它无法推断 a 的具体类型,但是如何使用 Show 来声明它 .

我知道我可以这样解决上述问题: -

pmn :: Char->String
pmn 'g'="wow"

但我只是想正确理解 Show 类型类

5 回答

  • 3

    List确实实现 Show 类型类,但当你说: Show a => a -> String 这意味着该函数将接受任何实现 Show 的类型 . 最重要的是,你只能调用show class函数,你的函数永远不会知道 a 的具体类型 . 而你试图在 a 上调用列表模式匹配

    Update for new edit in question:

    正确的实现将是: pm c ="wow" . 您可以在参数 c 上调用任何 Show 类型的类函数 . 您不能像以前那样进行模式匹配,因为您不知道参数的确切类型,您只知道它实现了 Show 类型类 . 但是当您将 Char 特定为类型时,模式匹配才起作用

  • 1

    在这两个签名中, a isn 't a list -- it'是任何类型,你不能选择哪个(除了它必须是 Show 的实例) .

    tell₁ :: Show a => [a] -> String
    tell₁ [] = "The list is empty"
    ... -- (remember to match the non-empty list case too!)
    

    您在 a 的列表上匹配,而不是在 a 类型的值上匹配 .

    如果你写的

    tell₂ :: Show a => a -> String
    tell₂ [] = "The list is empty"
    ...
    

    你会假设类型 a 是列表的类型(东西) . 但它可以是任何类型,例如 Bool .

    (但是我可能不理解你的问题 - 你还没有真正说出问题所在 . 当问这样的问题时,你应该通常指明你做了什么,你期望什么,发生了什么 . 这些都不是这里确实指明了,所以人们只能猜测你的意思 . )

  • 1

    问题不在于 Show . 确实,如果我们尝试:

    tell2 :: a -> String
    tell2 [] = "The list is empty"
    

    我们得到了类型检查错误 . 让我们看看它的内容:

    test.hs:5:7:
        Couldn't match expected type `a' with actual type `[t0]'
          `a' is a rigid type variable bound by
              the type signature for tell2 :: a -> String at test.hs:4:10
        In the pattern: []
        In an equation for `tell2': tell2 [] = "The list is empty"
    

    现在我们问自己,这个所谓的'type'构造真的意味着什么?当你写 tell2 :: a -> String 时,你所说的是对于任何类型的确切的a, tell2 会给我们一个 String . [a] (或 [c][foo] - 名称无关紧要)并不完全是 a . 这似乎是一种随意的区分,据我所知,它是 . 让我们看看写作时会发生什么

    tell2 [] = "The list is empty"
    
    > :t tell2
    > tell2 :: [t] -> [Char]
    

    如你所知,写 ta 之间没有区别, [Char] 只是 String 的类型同义词,所以我们写的类型和GHC类型的推断是 identical .

    嗯,不太好 . 当您自己,程序员在源中手动指定函数的类型时,类型签名中的类型变量将变得僵化 . 这究竟是什么意思?

    来自https://research.microsoft.com/en-us/um/people/simonpj/papers/gadt/

    “而不是”用户指定的类型“,我们使用简短的术语刚性类型来描述一种完全由程序员提供的类型注释以某种直接方式指定的类型 . ”因此,刚性类型是程序员类型签名指定的任何类型 . 所有其他类型都“颤抖”[1]

    所以,只是因为你写出来的事实,类型签名变得不同了 . 在这种新型语法中,我们有 a /= [b] . 对于刚性类型签名,GHC将推断它可以提供的信息量最少 . 它必须从模式绑定推断 a ~ [b] ;但是它不能从您提供的类型签名中进行推断 .

    让我们看看GHC为原始函数提供的错误:

    test.hs:2:6:
        Could not deduce (a ~ [t0])
        from the context (Show a)
          bound by the type signature for tell :: Show a => a -> String
          at test.hs:1:9-29
          `a' is a rigid type variable bound by
    

    我们再次看到 rigid type variable 等,但在这种情况下,GHC还声称它无法推断出某些东西 . (顺便说一下 - 类型语法中的 a ~ b === a == b ) . 类型检查器实际上是在类型中查找使函数有效的约束;它没有找到它并且足够好以告诉你它需要使它有效:

    {-# LANGUAGE GADTs #-}
    tell :: (a ~ [t0], Show a) => a -> String
    tell [] = "The list is empty"
    

    如果我们插入GHC 's suggestion verbatim, it type checks, since now GHC doesn' t需要进行任何推断;我们已经准确地告诉了 a 是什么 .

  • 3

    一旦你在'g'上模式匹配,例如

    pm 'g' = "wow"
    

    你的函数不再具有 (Show a) => a -> String 的类型;相反,它具有'a'的具体类型,即Char,因此它变为 Char -> String

    这与您给出的显式类型签名直接冲突,表明你的函数适用于任何类型'a'(只要该类型是 Show 的实例) .

    在这种情况下,您无法模式匹配,因为您在Int,Char等上进行模式匹配 . 但您可以在Prelude中使用 show 函数:

    pm x = case show x of
                 "'g'" -> "My favourite Char"
                 "1"   -> "My favourite Int" 
                 _     -> show x
    

    你可能已经猜到了, show 有点神奇;) . 实际上,为每个类型实现了一大堆 show 函数,它们是 Show 类型类的实例 .

  • 3
    tell :: (Show a) =>a->String
    

    这表示 tell 接受可显示的任何类型 a 的值 . 你可以在任何可以看到的东西上调用它 . 这意味着在 tell 的实现中,你必须能够对任何事物进行操作(这是可以显示的) .

    您可能认为这对于该类型签名来说是一个好的实现:

    tell [] = "The list is empty"
    

    因为列表确实可以显示,所以第一个参数的有效值也是如此 . 但是如果我打电话给 tell 1tell Truetell (1, 'c') 等,我就有意义了 .

    tell 内, a 类型可以是 Show 实例的任何类型 . 所以我能用这个值做的唯一事情就是对所有类型_1144034_有效的事情 . 这基本上意味着您只能将其传递给具有通用 Show a => a 参数的其他类似函数

    您的混淆源于对类型签名 tell :: (Show a) => [a] -> String 的误解"Considering that a here is a list and not an element of the list" . 这里 a 实际上是列表的一个元素,而不是列表本身 .

    该类型签名读取"tell takes a single paramter, which is a list of some showable type, and returns a string" . 这个版本的 tell 知道它接收一个列表,所以它可以用它的参数做一些列表 . 列表中的内容是某些未知类型的成员 .


    1除了将值传递给另一个 Show 函数之外,大多数这些函数也无法对值进行任何操作,但迟早会忽略该值或将其传递给 Show 类型类中的一个实际函数;它们具有针对每种类型的专用实现,因此每个专用版本都可以知道它正在运行的类型,这是最终可以完成的唯一方式 .

相关问题