【问题标题】:Help with possible Haskell type inference quiz questions帮助解决可能的 Haskell 类型推理测验问题
【发布时间】:2010-05-11 21:03:23
【问题描述】:
foldr:: (a -> b -> b) -> b -> [a] -> b
map :: (a -> b) -> [a] -> [b]
mys :: a -> a
(.) :: (a -> b) -> (c -> a) -> c -> b

什么是推断类型:
a.map 我的 ::
b.mys 地图 ::
c.foldr 地图 ::
d.foldr map.mys ::

我尝试使用 mys n = n + 2 自己创建 mys,但它的类型是

mys :: Num a => a -> a

'Num a => a -> a' 和 just 'a -> a' 有什么区别?或者'Num a =>'是什么意思?是不是 mys 只会取 Num 类型?

无论如何,
a) 我得到 [a] -> [a] 我想是因为它只需要一个列表并根据我对 mys 的定义返回 +2'd
b) (a -> b) -> [a] -> [b] 我认为因为 map 仍然需要像 (*3) 这样的两个参数和一个列表然后返回 [a] 到 mys 并返回 [b] c) 我不知道该怎么做 1.
d) 我也不确定如何执行此操作 1 但 map.mys 表示先做 mys 然后再映射对吗?

我的答案和想法正确吗?如果没有,为什么不呢?

谢谢!

【问题讨论】:

    标签: haskell type-inference inferred-type


    【解决方案1】:

    (更新:显然我的原始答案对 OP 不是很有帮助...请参阅下面的第二个 HR 以获取附录。)


    在这种情况下,你真正想做的是启动ghci并使用:t找出各种表达式的类型。例如

    :t foldr map
    -- answer: foldr map :: [b] -> [b -> b] -> [b]
    

    如果您需要先定义名称,请使用 let(在 Haskell 源文件中不需要):

    let mys = undefined :: a -> a
    :t map mys
    -- answer: [a] -> [a]
    

    注意undefined 的使用带有显式类型签名。找出各种形式的表达式类型是完全可以的,甚至可能在实际代码中用作早期规划阶段的占位符。

    Num a =>a 上的类型类约束;参见例如Type Classes and Overloading 来自 "A Gentle Introduction to Haskell, Version 98" 或 Chapter 6. Using Typeclasses 来自 "Real World Haskell" 以获取更多信息。 (基本上,它做你认为它做的事情。:-))


    以上内容对于验证您的答案应该很有用(并且类型类的资源很好)。至于如何自己解决这类问题:

    类型推断是所谓的“统一算法”的应用。谷歌“统一”了一些资源;如果您在查询中添加编程语言的名称,您可能会找到示例实现。

    至于如何处理手头的例子......

    一个。 map mysmap 接受 a -> b 类型的函数并返回 [a] -> [b] 类型的函数。一般来说,a 可以不同于b,但mys 的类型是a -> a,所以返回的函数将是[a] -> [a] 类型。

    这里有一些手统一:

    (a -> b) -> [a] -> [b]`  
    (a -> a)`
          ^-- at this point we unify a with b;
              when propagated to the return type,
              this produces [a] -> [a]
    

    b. mys mapmys 是一个函数,它接受某种类型的对象并返回相同类型的对象。特别是,如果您将(a -> b) -> [a] -> [b] 类型的参数传递给它,那将是返回值的类型。

    顺便说一句,只有一个“有趣的”(不是undefined)函数,其类型签名是a -> a(没有类型类约束),即id。请参阅 Philip Wadler 的论文“免费定理!” (您可以从this page 下载)以进行扩展讨论。

    c。 foldr map:首先,注意foldr签名中的as和bs与map签名中的那些无关。将mapa 重命名为c 并将b 重命名为d 并使用签名map :: (c -> d) -> [c] -> [d] 可能会很方便,以便在下面更清楚地看到这一点。另请注意,(a -> b -> b) 只是写(a -> (b -> b)) 的一种更简单的方式。

    更多手统一(解释如下):

    (a       -> (b  -> b))
    (c -> d) -> [c] -> [d]
    

    这里发生的是foldr 接受(a -> (b -> b)) 类型的函数参数。如果您将map 作为该参数传入,a 将与c -> d 统一,b[c] 统一,然后再次与[d] 统一,这意味着c 等于d

    foldr的返回值有b -> [a] -> b类型;替换上一段中获得的更具体的类型,我们得到

    [c] -> [c -> c] -> [c]
                 ^-- c equals d, right?
    

    c 来自我们为map 更改的签名;用原来的b,就变成了

    [b] -> [b -> b] -> [b]
    

    d。 foldr map . mys:这其实是(foldr map) . mys,不是foldr (map . mys)——函数应用(“隐形算子”)绑定最强!结合一个。和 c。从上面解决这个问题留给读者作为练习。 ;-)

    【讨论】:

    • 谢谢,但我在试卷上写 :t 不会给我答案。呵呵。这就是为什么我试图了解它为什么是 [b] -> [b->b] -> [b]
    • 哦,我明白了。希望我新编辑的答案会更有帮助。
    • 我得到了“d”的 [a]->[a->a]->[a]。 :t 检查是正确的,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 2012-01-18
    • 2013-01-28
    • 2011-02-26
    • 1970-01-01
    相关资源
    最近更新 更多