简体   繁体   English

Haskell和一般类型

[英]Haskell and General Types

I'm kind of new to Haskell , and I would like to understand how general types works. 我是Haskell的新手,我想了解一般类型的工作原理。

What should be a "systematic" way of thinking to get the type of an expression? 什么应该是一种“系统的”思维方式来获得表达的类型?

To make an example, if we have: 举个例子,如果我们有:

(\x y z -> x (y z))

The way I would think about this, would just be to use intuition, but it doesn't work always. 我想到的方式,只是使用直觉,但它总是不起作用。

In this case I would say: 在这种情况下,我会说:

   (y z) :: (t -> t1) --function y takes t and return t1
 x (y z) :: (t1 -> t2) --function x takes argument of type (return type of y)

(\x y z -> x (y z)) :: (t1 -> t2) -> (t -> t1) -> t -> t2 --return type is t2 (of x) with argument of type t for function y

I'm pretty sure this should be right, but sometimes it's harder and this way go thinking doesn't seems to work. 我很确定这应该是正确的,但有时它更难以这种方式去思考似乎不起作用。

For example if we have: 例如,如果我们有:

1.  (\x -> x (<))  or
2.  (.) . (.)

In this cases I would have no idea how to find the types, for the first I would guess that (<) is a function on two elements that returns a Bool but I have troubles writing the hole expression. 在这种情况下,我不知道如何找到类型,因为我猜第一个(<)是两个返回Bool元素的函数,但是我在编写孔表达式时遇到了麻烦。

So the question is, what is the best way work with this kind of exercises? 所以问题是,使用这种练习的最佳方法是什么?


ADDED: I know how to check the types with ghci , the question was how to actually find them without it (to understand how Haskell works). 补充:我知道如何用ghci检查类型,问题是如何在没有它的情况下实际找到它们(了解Haskell如何工作)。

You can easily check the type of an expression with :t in ghci : 您可以使用以下命令轻松检查表达式的类型:t in ghci

Prelude> :t (\x y z -> x (y z))
(\x y z -> x (y z)) :: (r1 -> r) -> (r2 -> r1) -> r2 -> r
Prelude> :t (\x -> x (<))
(\x -> x (<)) :: Ord a => ((a -> a -> Bool) -> r) -> r
Prelude> :t (.) . (.)
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c

But I assume you want to derive the types yourself. 但我认为你想自己推导出这些类型。

  1. For the first we can start with looking to the variables. 首先,我们可以从查看变量开始。 Those are x , y and z . 那些是xyz We first assign "generic types" to them, so: 我们首先为它们分配“泛型类型”,因此:

     x :: a y :: b z :: c 

    Now we look to the right hand of the expression and see (yz) . 现在我们查看表达式的右侧并查看(yz) This means that we "call" y with z as argument. 这意味着我们用z作为参数“调用” y As a result we "specialize" the type of y to y :: c -> d . 因此,我们“专注”的类型yy :: c -> d The type of (yz) then is (yz) :: d . 的类型的(yz)然后是(yz) :: d Now we see that x (yz) . 现在我们看到x (yz) So again we "specialize" the type of x into x :: d -> e . 所以我们再次将x的类型“特化”为x :: d -> e As a result we obtain: 结果我们得到:

     x :: d -> e y :: c -> d z :: c 

    Finally the lambda-expression maps \\xyz -> x (yz) . 最后,lambda表达式映射\\xyz -> x (yz) So that means we look to the resulting type is something in "pseudo code": type(x) -> type(y) -> type(z) -> type('x (yz)') . 所以这意味着我们在“伪代码”中查看结果类型: type(x) -> type(y) -> type(z) -> type('x (yz)') Or: 要么:

     \\xyz -> x (yz) :: (d -> e) -> (c -> d) -> c -> e 
  2. For the second expression we first have to derive the type of (<) : 对于第二个表达式,我们首先必须导出(<)的类型:

     Prelude> :t (<) (<) :: Ord a => a -> a -> Bool 

    This is because (<) is somewhere defined in the Prelude with that type. 这是因为(<)Prelude使用该类型定义的某个地方。

    Now we know that, we can look at the type signature. 现在我们知道了,我们可以看一下类型签名。 We will first assume a type for x :: b . 我们将首先假设x :: b的类型。 Now we look to the right side, and see that we call x (<) . 现在我们向右看,看到我们调用x (<) So that means we know x has the type Ord a => (a -> a -> Bool) -> c and we know that x (<) :: c . 所以这意味着我们知道x的类型为Ord a => (a -> a -> Bool) -> c ,我们知道x (<) :: c Then again we have a lambda-expression, and as we have seen above, we can resolve it like: 然后我们再次有一个lambda表达式,正如我们上面所看到的,我们可以解决它:

     (\\x -> x (<)) :: Ord a => ((a -> a -> Bool) -> c) -> c 
  3. Finally for the third expression, let is first rewrite it to: 最后,对于第三个表达式,让我们先将其重写为:

     (.) (.) (.) 

    Here the first (.) function, was originally the . 这里的第一个 (.)函数原来是. (without brackets), but we will first remove the operator syntactical sugar. (没有括号),但我们将首先删除操作符语法糖。

    Next we will give the operators a name (only to make it more convenient for us to name them). 接下来我们将给操作员一个名字(只是为了让我们更方便地命名它们)。 This is not allowed in Haskell, but we will ignore that for now. 不是在Haskell允许的,但我们会忽视现在。 The reason we do this is to later refer to the type of a specific (.) function: 我们这样做的原因是稍后引用特定(.)函数的类型:

     (. 1 ) (. 2 ) (. 3 ) 

    Next we lookup the type for (.) : 接下来我们查找(.)的类型:

     Prelude> :t (.) (.) :: (b -> c) -> (a -> b) -> a -> c 

    So we first assign some types: 所以我们先分配一些类型:

     (.1) :: (b -> c) -> (a -> b) -> a -> c (.2) :: (e -> f) -> (d -> e) -> d -> f (.3) :: (h -> i) -> (g -> h) -> g -> i 

    Now we need to analyze these types further. 现在我们需要进一步分析这些类型。 We call (.1) with (.2) as argument so we know that (b -> c) (the first argument of (.1) is equivalent to (e -> f) -> (d -> e) -> d -> f . So that means that: 我们用(.2)作为参数调用(.1) ,因此我们知道(b -> c)(.1)的第一个参数等价于(e -> f) -> (d -> e) -> d -> f 。这意味着:

     (b -> c) ~ (e -> f) -> (d -> e) -> d -> f 

    Since the type arrows are right associative , (e -> f) -> (d -> e) -> d -> f actually means (e -> f) -> ((d -> e) -> (d -> f)) . 由于类型箭头是右关联的(e -> f) -> (d -> e) -> d -> f实际上意味着(e -> f) -> ((d -> e) -> (d -> f)) So now we can make an analysis: 所以现在我们可以做一个分析:

      b -> c ~ (e -> f) -> ((d -> e) -> (d -> f)) 

    So that means b ~ (e -> f) and c ~ ((d -> e) -> (d -> f)) . 这意味着b ~ (e -> f)c ~ ((d -> e) -> (d -> f)) So we "specialize" our first (.1) :: (b -> c) -> (a -> b) -> a -> c into 所以我们“专门化”我们的第一个(.1) :: (b -> c) -> (a -> b) -> a -> c进入

     (.1) :: ((e -> f) -> ((d -> e) -> (d -> f))) -> (a -> (e -> f)) -> a -> ((d -> e) -> (d -> f)) 

    Now the type of (.1) (.2) is 现在(.1) (.2)的类型是

     (.1) (.2) :: (a -> (e -> f)) -> a -> ((d -> e) -> (d -> f)) 

    But now we call that function with (.3) so we have to derive the type for ((.1) (.2)) (.3) . 但现在我们用(.3)调用该函数,所以我们必须派生((.1) (.2)) (.3) Therefore again we have to do type resolution with: 因此,我们必须进行类型解析:

      a -> (e -> f) ~ (h -> i) -> ((g -> h) -> (g -> i)) 

    So that means a ~ (h -> i) , e ~ (g -> h) and f ~ (g -> i) . 所以这意味着a ~ (h -> i)e ~ (g -> h)f ~ (g -> i) So now we have resolved the type to: 所以现在我们已经将类型解析为:

     ((.1) (.2)) (.3) :: a -> c = (h -> i) -> ((d -> (g -> h)) -> (d -> (g -> i))) = (h -> i) -> (d -> g -> h) -> d -> g -> i 

    The last line is only a syntactical simplification. 最后一行只是一种语法简化。 As you can see, this maps on the type we derived from ghci . 如您所见,这映射了我们从ghci派生的类型。

As you can see for all three queries we obtain the same type (of course the name of the type variables is different). 正如您可以看到的所有三个查询我们获得相同的类型(当然类型变量的名称是不同的)。

Using the GHC compiler, you can ask about type by using type holes, _ 使用GHC编译器,您可以通过使用类型孔询问类型, _

for instance: 例如:

thing :: _
thing = (\x -> (<))

yields: 收益率:

Found hole ‘_’ with type: t -> a0 -> a0 -> Bool
Where: ‘t’ is a rigid type variable bound by
           the inferred type of thing :: t -> a0 -> a0 -> Bool
           at src/Expat/Data/E.hs:555:1
       ‘a0’ is an ambiguous type variable
To use the inferred type, enable PartialTypeSignatures
In the type signature for ‘thing’: _

I sometimes use this with where clauses to isolate expressions in larger functions, eg 我有时会在where子句中使用它来隔离更大函数中的表达式,例如

myFun :: a -> b
myFun a = otherFun thing
  where 
    thing :: _
    thing = (\x -> (<))

You can also use holes in place of terms. 您也可以使用孔代替术语。 For instance, if I'm not sure exactly what otherFun should be, I could just substitute in _ . 例如,如果我不确定otherFun到底应该是什么,我可以在_替换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM