Haskell和一般类型

时间:2017-03-21 13:26:43

标签: function haskell generics lambda types

我是 Haskell 的新手,我想了解一般类型的工作原理。

应该是什么"系统"获得表达类型的思维方式?

举个例子,如果我们有:

private Boolean downloadAndSaveFile(String server, int portNumber,String user, String password) throws IOException {

    FTPClient ftp = null;
    // path of a file which is on a web server
    String remoteFile1 = "/public_html/1/c language/Unit_1_HTML_and_Forms.pdf";
    // Path of my device
    File downloadFile1 = new File("/storage/emulated/0/a/new.pdf");
    try {
        ftp = new FTPClient();
        ftp.connect(server, portNumber);
        ftp.login(user, password);
        ftp.setFileType(FTP.BINARY_FILE_TYPE);
        ftp.enterLocalPassiveMode();
        System.out.println("Reached passive");
        OutputStream outputStream1 = new BufferedOutputStream(new FileOutputStream(downloadFile1));
        System.out.println("Stream Created");
        boolean success = ftp.retrieveFile(remoteFile1, outputStream1);
        System.out.println(success);
        return true;
    } catch(Exception e) {
        System.out.println(e);
    } finally {
        if (ftp != null) {
            ftp.logout();
            ftp.disconnect();
        }
    }
    return true;
}

我想到的方式,只是使用直觉,但它并不总是有效。

在这种情况下,我会说:

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

我很确定这应该是正确的,但有时它会更难以这种方式去思考似乎不起作用。

例如,如果我们有:

   (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

在这种情况下,我不知道如何找到类型,因为我猜第一个(&lt;)是两个返回1. (\x -> x (<)) or 2. (.) . (.) 的元素的函数但是我在编写孔表达式时遇到了麻烦

所以问题是,使用这种练习的最佳方法是什么?

补充:我知道如何使用Bool检查类型,问题是如何在没有它的情况下实际找到它们(了解Haskell如何工作)。

2 个答案:

答案 0 :(得分:7)

您可以使用:t中的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

但我认为你想自己推导出类型。

  1. 首先,我们可以从查看变量开始。这些是xyz。我们首先分配&#34;泛型类型&#34;他们,所以:

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

    现在我们查看表达式的右侧并查看(y z)。这意味着我们呼叫&#34;以y为参数的z。因此,我们专注于&#34; yy :: c -> d的类型。 (y z)的类型为(y z) :: d。现在我们看到x (y z)。所以我们再次专注于#34; x到[{1}}的类型。结果我们获得了:

    x :: d -> e

    最后lambda表达式映射x :: d -> e y :: c -> d z :: c 。所以这意味着我们在结果类型中查找&#34;伪代码&#34;:\x y z -> x (y z)。或者:

    type(x) -> type(y) -> type(z) -> type('x (y z)')
  2. 对于第二个表达式,我们首先必须派生\x y z -> x (y z) :: (d -> e) -> (c -> d) -> c -> e 的类型:

    (<)

    这是因为Prelude> :t (<) (<) :: Ord a => a -> a -> Bool (<)中使用该类型定义的地方。

    现在我们知道,我们可以查看类型签名。我们将首先假设Prelude的类型。现在我们向右看,看到我们称之为x :: b。这意味着我们知道x (<)的类型为x,我们知道Ord a => (a -> a -> Bool) -> c。然后我们再次得到一个lambda表达式,正如我们上面所看到的,我们可以解决它:

    x (<) :: c
  3. 最后,对于第三个表达式,让我们先将其重写为:

    (\x -> x (<)) :: Ord a => ((a -> a -> Bool) -> c) -> c
    

    这里的第一个 (.) (.) (.) 函数最初是(.)(没有括号),但我们将首先删除运算符语法糖。

    接下来,我们将给操作员一个名字(只是为了让我们更方便地命名)。这在Haskell中是 not ,但我们现在将忽略它。我们这样做的原因是稍后引用特定.函数的类型:

    (.)

    接下来,我们查找(.1) (.2) (.3)的类型:

    (.)

    所以我们先分配一些类型:

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

    现在我们需要进一步分析这些类型。我们以(.1) :: (b -> c) -> (a -> b) -> a -> c (.2) :: (e -> f) -> (d -> e) -> d -> f (.3) :: (h -> i) -> (g -> h) -> g -> i 作为参数调用(.1),因此我们知道(.2)(b -> c)的第一个参数等同于(.1)。这意味着:

    (e -> f) -> (d -> e) -> d -> f

    由于类型箭头是右关联(b -> c) ~ (e -> f) -> (d -> e) -> d -> f 实际上意味着(e -> f) -> (d -> e) -> d -> f。所以现在我们可以进行分析:

    (e -> f) -> ((d -> e) -> (d -> f))

    这意味着 b -> c ~ (e -> f) -> ((d -> e) -> (d -> f)) b ~ (e -> f)。所以我们专注于#34;我们的第一个c ~ ((d -> e) -> (d -> f))进入

    (.1) :: (b -> c) -> (a -> b) -> a -> c

    现在(.1) :: ((e -> f) -> ((d -> e) -> (d -> f))) -> (a -> (e -> f)) -> a -> ((d -> e) -> (d -> f)) 的类型是

    (.1) (.2)

    但现在我们用(.1) (.2) :: (a -> (e -> f)) -> a -> ((d -> e) -> (d -> f)) 调用该函数,因此我们必须派生(.3)的类型。因此,我们必须使用以下方式进行类型解析:

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

    这意味着 a -> (e -> f) ~ (h -> i) -> ((g -> h) -> (g -> i)) a ~ (h -> i)e ~ (g -> h)。所以现在我们已经将类型解析为:

    f ~ (g -> i)

    最后一行只是一种语法简化。如您所见,这会映射我们从((.1) (.2)) (.3) :: a -> c = (h -> i) -> ((d -> (g -> h)) -> (d -> (g -> i))) = (h -> i) -> (d -> g -> h) -> d -> g -> i 派生的类型。

  4. 正如您在三个查询中看到的那样,我们获得了相同的类型(当然,类型变量的名称也不同)。

答案 1 :(得分:2)

使用GHC编译器,您可以使用类型孔_

询问类型

例如:

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

的产率:

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’: _

我有时会在where子句中使用它来隔离较大函数中的表达式,例如

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

您也可以使用孔代替术语。例如,如果我不确定其他应该是什么,我可以在_中替换。