Haskell语法:函数如何组合在一起?

时间:2017-10-28 13:04:28

标签: haskell

来自FileIO.hs

Functional Programming Course exercise的一行
while($row = $sql->fetchObject()){
    echo $row->mail;
    echo '<form ...><input name="contactMail" value="' . $row->mail . '"><...submit>
    <input type="hidden" name="itemId" value="' . $row->id . '"></form>';
    if(isset($_POST['contactMail']) && $_POST['itemId'] == $row->id) {
        mail($toRowMail,$subject,$body,$headers);
    }
}

根据其类型签名,getFile :: FilePath -> IO (FilePath, Chars) getFile = lift2 (<$>) (,) readFile 返回getFile,这意味着文件名及其内容的元组。

但我无法弄清楚为什么会这样。

为什么左侧IO ( FilePath, Chars)未更改,右侧填充FilePath文件名?

readFile也是一个适用的实例吗? (,)不是(,),那么IO举起了什么?

而且,有没有办法推导出那些类型的签名并得到证明?

我知道的语法是一个函数遵循它的参数,它在右边吃一个参数并成为一个新函数。但是当涉及到这样的代码时,它看起来就像是一个神奇的立方体......

感谢您帮助我!

聚苯乙烯。额外信息如下

lift2

1 个答案:

答案 0 :(得分:4)

让我们来看看

lift2 (<$>) (,) readFile

这确实是简单的功能应用程序:

((lift2 (<$>)) (,)) readFile

(或lift2应用于三个参数)。

涉及的类型(使用唯一重命名的类型变量来减少混淆)是:

lift2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
(<$>) :: (Functor g) => (j -> k) -> g j -> g k
(,) :: m -> n -> (m, n)
readFile :: FilePath -> IO Chars

我们表达的第一件事是将lift2应用于(<$>)。这意味着我们需要以某种方式统一a -> b -> clift2的第一个参数的类型)和(Functor g) => (j -> k) -> g j -> g k<$>的类型)。

那是:

a -> b -> c = (j -> k) -> g j -> g k
-- where g is a Functor
a = j -> k
b = g j
c = g k

这样就可以了。结果类型是

f a -> f b -> f c
-- where f is an Applicative

f (j -> k) -> f (g j) -> f (g k)

现在,此表达式(lift2 (<$>))已应用于(,)。我们必须再次排列类型:

f (j -> k) = m -> n -> (m, n)

这里我们使用->是正确关联的属性(即a -> b -> c表示a -> (b -> c)),并且我们可以在类型中使用(curried)前缀表示法(即{{1 }}与a -> b相同,与(->) a b)相同。

((->) a) b

这也很有用。结果类型是

f (j -> k) = ((->) m) (n -> (m, n))
f = (->) m
j = n
k = (m, n)

(替换后)成为

f (g j) -> f (g k)

此表达式(((->) m) (g n) -> ((->) m) (g (m, n)) (m -> g n) -> (m -> g (m, n)) )适用于lift2 (<$>) (,)。再次,使类型排队:

readFile

并替换为结果类型:

m -> g n = FilePath -> IO Chars
m = FilePath
g = IO
n = Chars

这是整个m -> g (m, n) FilePath -> IO (FilePath, Chars) 表达式的类型。正如所料,它符合lift2 (<$>) (,) readFile的声明。

但是,我们仍需要验证我们的类约束(getFile :: FilePath -> IO (FilePath, Chars)Functor g)是否已解决。

Applicative fg,确实是IO(以及FunctorApplicative)。这里没有什么大惊喜。

Monad更有趣:f,因此我们需要为f = (->) m寻找Applicative个实例。事实上这样的实例确实存在,其定义包含(->) m实际执行的内容的答案。

我们可以通过查看getFile的类型(在lift2中使用)来推导实例的样子:

getFile

即。 lift2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c lift2 :: (a -> b -> c) -> ((->) m) a -> ((->) m) b -> ((->) m) c lift2 :: (a -> b -> c) -> (m -> a) -> (m -> b) -> (m -> c) lift2 :: (a -> b -> c) -> (m -> a) -> (m -> b) -> m -> c 需要

  • lift2a合并为b的函数,
  • c转换为m
  • 的函数
  • a转换为m
  • 的函数
  • b

并生成m

它能做到这一点的唯一方法是将c传递给第二和第三个函数,并使用第一个函数组合它们的结果:

m

如果我们在lift2 f g h x = f (g x) (h x) 中内联这个定义,我们就会得到

getFile

读者的练习:

getFile = lift2 (<$>) (,) readFile getFile = \x -> (<$>) ((,) x) (readFile x) getFile = \x -> (,) x <$> readFile x 实际上是根据lift2<$>定义的。 <*> <$>实例中的<*>Applicative有哪些类型?他们的定义必须是什么样的?