当我在Haskell中用*编写时会发生什么?

use*_*425 45 haskell functional-programming function-composition

我试图了解结果

(*) . (+) 
Run Code Online (Sandbox Code Playgroud)

在哈斯克尔.我知道合成算子只是数学函数的标准组成 - 所以

(f . g) = f (g x)
Run Code Online (Sandbox Code Playgroud)

但:

(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
Run Code Online (Sandbox Code Playgroud)

我很难理解这种类型的签名.我希望能够做到这样的事情:

((*) . (+)) 1 2 :: Num a => a -> a
= (* (+ 1 2))
Run Code Online (Sandbox Code Playgroud)

的意义是什么 (*) .(+)的签名?我尝试用它来玩它(只是匹配它的签名):

((*) . (+)) 1 (\x -> x + 1) 1
Run Code Online (Sandbox Code Playgroud)

但那无法编译.我正在尝试在编写这些步骤时完成逻辑步骤,但我还没有完全理解它是如何得到这个结果的(以及结果什么).

Aad*_*hah 67

我理解你的感受.我发现功能组成起初也很难掌握.什么帮助我解决这个问题是类型签名.考虑:

(*) :: Num x => x -> x -> x
(+) :: Num y => y -> y -> y
(.) :: (b -> c) -> (a -> b) -> a -> c
Run Code Online (Sandbox Code Playgroud)

现在当你写(*) . (+)它时实际上是相同的(.) (*) (+)(即(*)第一个参数(.)(+)第二个参数(.)):

(.) :: (b -> c) -> (a -> b) -> a -> c
       |______|    |______|
           |           |
          (*)         (+)
Run Code Online (Sandbox Code Playgroud)

因此,(*)(即Num x => x -> x -> x)的类型签名与b -> c:

(*) :: Num x => x -> x -> x -- remember that `x ->  x -> x`
                |    |____| -- is implicitly `x -> (x -> x)`
                |       |
                b ->    c

(.) (*) ::          (a -> b) -> a ->    c
                          |             |
                          |          |????|
           Num x =>       x          x -> x

(.) (*) :: Num x => (a -> x) -> a -> x -> x
Run Code Online (Sandbox Code Playgroud)

因此,(+)(即Num y => y -> y -> y)的类型签名与Num x => a -> x:

(+) :: Num y => y -> y -> y -- remember that `y ->  y -> y`
                |    |____| -- is implicitly `y -> (y -> y)`
                |       |
       Num x => a ->    x

(.) (*) (+) ::  Num x                => a ->     x    ->    x
                                        |        |          |
                                        |     |????|     |????|
                              Num y  => y     y -> y     y -> y

(.) (*) (+) :: (Num (y -> y), Num y) => y -> (y -> y) -> y -> y
Run Code Online (Sandbox Code Playgroud)

我希望澄清其中Num (y -> y)Num y从何而来.你留下了一个非常奇怪的类型的功能(Num (y -> y), Num y) => y -> (y -> y) -> y -> y.

让它变得如此奇怪的原因在于它既有y并且y -> y也有实例Num.可以理解y应该是一个实例Num,但是如何y -> y?制作y -> y一个Num似乎不合逻辑的例子.这不可能是正确的.

但是,当您查看函数组合实际执行的操作时,它是有意义的:

( f  .  g ) = \z ->  f  ( g  z)

((*) . (+)) = \z -> (*) ((+) z)
Run Code Online (Sandbox Code Playgroud)

所以你有一个功能\z -> (*) ((+) z).因此z必须明确是Num因为(+)适用于它的一个实例.这样的类型\z -> (*) ((+) z)Num t => t -> ...其中...的类型(*) ((+) z),我们将在稍后发现.

因此它((+) z)是类型的,Num t => t -> t因为它需要一个更多的数字.但是,在将其应用于其他数字之前,将应用于该数字(*).

因此(*)期望((+) z)成为一个实例Num,这就是为什么t -> t期望成为的实例Num.因此将...其替换为(t -> t) -> t -> t并且Num (t -> t)添加约束,从而产生类型(Num (t -> t), Num t) => t -> (t -> t) -> t -> t.

你真正想要结合(*)(+)使用的方式(.:):

(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
f .: g = \x y -> f (g x y)
Run Code Online (Sandbox Code Playgroud)

因此(*) .: (+)是一样的\x y -> (*) ((+) x y).现在提出两个论点来(+)确保这((+) x y)确实是公正的Num t => t而不是Num t => t -> t.

因此((*) .: (+)) 2 3 5,(*) ((+) 2 3) 5哪个是(*) 5 5哪个25,我认为是你想要的.

注意,f .: g也可以写为(f .) . g,(.:)也可以定义为(.:) = (.) . (.).你可以在这里读更多关于它的内容:

什么(f.).在Haskell中意味着什么?

希望有所帮助.


Syd*_*ove 9

(*)并且(+)都有类型签名Num a => a -> a -> a 现在,如果你编写它们,你会得到一些时髦的东西.

(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
Run Code Online (Sandbox Code Playgroud)

那是因为(*)(+)期待两个"论点".

(+)带一个参数可以获得一个函数.该.运营商预计功能(a -> a你看).

这是意思 (*) . (+)

                                       x     f          y
 (*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
Run Code Online (Sandbox Code Playgroud)

(*) . (+)映射x f y((x +) * f) y哪里f是从函数aa,这也是一个数字.原因是(*)期望一个函数是使类型匹配而它需要两个参数,但该函数必须是一个数字,因为(*)它只适用于数字.

真的,这个功能毫无意义.


use*_*465 7

首先是一些扩展:

{-# LANGUAGE FlexibleContexts, FlexibleInstances, TypeSynonymInstances #-}
Run Code Online (Sandbox Code Playgroud)

正如其他答案所示,你的功能是

weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
weird x g = (x +) * g
Run Code Online (Sandbox Code Playgroud)

但是这个函数确实有非奇怪的语义.

有一个差异列表的概念.因此,存在差异整数的概念.我已经看到它们仅在依赖类型的设置中使用(例如,这里,但这不是唯一的情况).该定义的相关部分是

instance Enum DiffInt where
    toEnum   n = (n +)
    fromEnum n = n 0

instance Num DiffInt where
    n + m = n . m
    n * m = foldr (+) id $ replicate (fromEnum n) m
Run Code Online (Sandbox Code Playgroud)

这在Haskell中没有多大意义,但对于依赖类型可能很有用.

现在我们可以写了

test :: DiffInt
test = toEnum 3 * toEnum 4
Run Code Online (Sandbox Code Playgroud)

要么

test :: DiffInt
test = weird 3 (toEnum 4)
Run Code Online (Sandbox Code Playgroud)

在这两种情况下fromEnum test == 12.

编辑

可以避免使用TypeSynonymInstances扩展名:

{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}

weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
weird x g = (x +) * g

instance (Enum a, Num a) => Enum (a -> a) where
    toEnum   n = (toEnum n +)
    fromEnum n = fromEnum $ n (toEnum 0)

instance (Enum a, Num a) => Num (a -> a) where
    n + m = n . m
    n * m = foldr (+) id $ replicate (fromEnum n) m

type DiffInt = Int -> Int
Run Code Online (Sandbox Code Playgroud)

和以前一样,我们可以写

test' :: DiffInt
test' = weird 3 (toEnum 4)
Run Code Online (Sandbox Code Playgroud)

但现在我们也可以写

-- difference ints over difference ints
type DiffDiffInt = DiffInt -> DiffInt

test'' :: DiffDiffInt
test'' = weird (toEnum 3) (toEnum (toEnum 4))
Run Code Online (Sandbox Code Playgroud)

main = print $ fromEnum $ fromEnum test'
Run Code Online (Sandbox Code Playgroud)

打印12.

EDIT2添加了更好的链接.