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
答案 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 -> c
(lift2
的第一个参数的类型)和(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 f
是g
,确实是IO
(以及Functor
和Applicative
)。这里没有什么大惊喜。
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
需要
lift2
和a
合并为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
有哪些类型?他们的定义必须是什么样的?