Haskell 如何从数学角度查看高阶函数和IO操作?

Haskell 如何从数学角度查看高阶函数和IO操作?,haskell,functional-programming,io,higher-order-functions,purely-functional,Haskell,Functional Programming,Io,Higher Order Functions,Purely Functional,我试图从第一原理来理解函数式编程,但我仍然停留在纯函数式世界和具有状态和副作用的不纯净现实世界之间的界面上。从数学角度看, 什么是返回函数的函数 返回IO操作(如Haskell的IO类型)的函数是什么 详细说明:在我的理解中,纯函数是从一个域到另一个域的映射。最终,它是从计算机内存中的一些值到内存中其他一些值的映射。在函数式语言中,函数是以声明方式定义的;i、 例如,它们描述映射,但不描述需要对特定输入值执行的实际计算;后者由编译器派生。在一个有空闲内存的简化设置中,运行时将不会进行计算;相

我试图从第一原理来理解函数式编程,但我仍然停留在纯函数式世界和具有状态和副作用的不纯净现实世界之间的界面上。从数学角度看,

  • 什么是返回函数的函数
  • 返回IO操作(如Haskell的IO类型)的函数是什么
详细说明:在我的理解中,纯函数是从一个域到另一个域的映射。最终,它是从计算机内存中的一些值到内存中其他一些值的映射。在函数式语言中,函数是以声明方式定义的;i、 例如,它们描述映射,但不描述需要对特定输入值执行的实际计算;后者由编译器派生。在一个有空闲内存的简化设置中,运行时将不会进行计算;相反,编译器可以在编译时为每个函数创建一个查找表。执行纯程序将相当于表查找。因此,组合函数相当于构建高维查找表。当然,拥有计算机的全部目的是设计方法来指定函数,而不需要逐点查表——但我发现心智模型有助于区分纯函数和效果。然而,我很难将这种思维模式应用于高阶函数:

(||) x =  case x of True  -> let f y =  case y of True  -> True
                                                  False -> True
                             in f

                    False -> let g y =  case y of True  -> True
                                                  False -> False
                             in g

  • 对于以另一个函数为参数的函数,将值映射到值的结果一阶函数是什么?有数学描述吗(我肯定有,但我既不是数学家也不是计算机科学家)
  • 一个返回函数的函数怎么样?我如何在心里“展平”这个构造,再次得到一个一阶函数,将值映射到值
现在回到肮脏的现实世界。与它的交互不是纯粹的,但是没有它,就没有合理的程序。在我上面的简化心智模型中,分离程序的纯部分和非纯部分意味着每个功能程序的基础是一层命令语句,这些命令语句从现实世界获取数据,对其应用纯函数(执行表格查找),然后将结果写回现实世界(磁盘、屏幕、网络等)


在Haskell中,这种与现实世界的命令式交互被抽象为IO操作,编译器根据它们的数据依赖性对其进行排序。然而,我们并不直接将程序作为命令IO操作序列来编写。相反,有些函数返回IO操作(类型为
::IO a
)的函数)。但据我理解,这些不可能是真正的功能。它们是什么?如何根据上面概述的心智模型更好地思考它们?

从数学上讲,接受或返回其他函数的函数根本没有问题。从集合S到集合T的函数的标准集合论定义如下:

f∈ s→ T意味着f⊂ s✕ T和两个条件成立:

  • 如果是∈ S、 然后(S,t)∈ f代表一些t,和
  • 如果两者都是(s,t)∈ f和(s,t′)∈ f、 然后t=t’
  • 我们写f(s)=t作为(s,t)的方便的符号速记∈ f

    那么,写什么呢→ T只表示一个特定的集合,因此(a→ (B)→ C和A→ (B)→ C) 同样,它们只是特定的集合

    当然,为了提高效率,我们不会将内存中的函数表示为这样的一组输入输出对,但这是一个不错的第一近似值,如果需要数学直觉,可以使用它。(第二种近似需要更多的工作才能正确设置,因为它使用了您可能还没有经历过的结构,以谨慎、有原则的方式处理惰性和递归。)

    IO操作有点棘手。你想如何看待它们可能在一定程度上取决于你特定的数学倾向

    数学家的一种观点可能会将IO动作定义为归纳集,例如:

    • 如果
      x::a
      ,则
      纯x::IO a
    • 如果
      f::a->b
      ,则
      fmap f::IO a->IO b
    • 如果
      x::ioa
      f::a->iob
      ,则
      x>=f::iob
    • putStrLn::String->IO()
    • forkIO::IO a->IO线程ID
    • …还有上千个基本案例
    • 我们在几个等式上求商:
      • fmap id=id
      • fmap f。fmap g=fmap(f.g)
      • pure x>>=f
        =
        fx
      • x>>=pure。f
        =
        fmap f x
      • (读起来有点复杂,只是说
        >=
        是关联的)
    就定义程序的含义而言,这足以指定IO类型系列可以包含哪些“值”。您可能会从定义自然数的标准方式中认识到这种定义方式:

    • 零是一个自然数
    • 如果n是一个自然数,那么Succ(n)是一个自然数
    当然,这种定义事物的方法有些地方并不令人非常满意。比如:任何特定的IO操作意味着什么?这个定义中没有任何关于这一点的内容。(尽管请参阅“处理尴尬的团队”,以了解即使您采用这种类型的归纳定义,您如何说出IO操作的含义。)

    另一类数学家可能更喜欢这种定义:

    IO操作同构于表示宇宙当前状态的幻象令牌上的有状态函数:

    IO a ~= RealWorld -> (RealWorld, a)
    
    这种定义也有吸引力;不过,值得注意的是,很难说forkIO对这种定义到底做了什么

    putStrLn s = PutStr (s ++ "\n") (Return ())
    
    -- this is actually read <$> getLine; real readLn throws exceptions instead of returning bottoms
    readLn = GetLine (\s -> Return (read s))
    
    infixr 2 ||
    
    (||)           :: Bool -> (Bool -> Bool)
    True  || True  =  True
    True  || False =  True
    False || True  =  True
    False || False =  False
    
    x || y =  case (x, y) of (True, True)   -> True
                             (True, False)  -> True
                             (False, True)  -> True
                             (False, False) -> False
    
    x || y =  case x of True  -> (case y of True  -> True
                                            False -> True)
    
                        False -> (case y of True  -> True
                                            False -> False)
    
    (||) x =  case x of True  -> let f y =  case y of True  -> True
                                                      False -> True
                                 in f
    
                        False -> let g y =  case y of True  -> True
                                                      False -> False
                                 in g
    
    +-------+-----------------------+
    | x     | (||) x                |
    +-------+-----------------------+
    | True  |                       |
    |       |   +-------+-------+   |
    |       |   | y     | f y   |   |
    |       |   +-------+-------+   |
    |       |   | True  | True  |   |
    |       |   +-------+-------+   |
    |       |   | False | True  |   |
    |       |   +-------+-------+   |
    |       |                       |
    +-------+-----------------------+
    | False |                       |
    |       |   +-------+-------+   |
    |       |   | y     | g y   |   |
    |       |   +-------+-------+   |
    |       |   | True  | True  |   |
    |       |   +-------+-------+   |
    |       |   | False | False |   |
    |       |   +-------+-------+   |
    |       |                       |
    +-------+-----------------------+
    
    forall a . (->) a
    
    unit     :: a -> (d -> a)
    unit x   =  \ u -> x
    
    bind     :: (d -> a) -> (a -> (d -> b)) -> (d -> b)
    bind m k =  \ u -> let x = m u in k x u
    
    instance Monad ((->) a) where
        return = unit
        (>>=)  = bind
    
    unit     :: a -> (d -> a)
    unit x   =  \ u -> x
    
    bind     :: (d -> a) -> (a -> (d -> b)) -> (d -> b)
    bind m k =  \ u -> let !x = m u in k x u
    
    next     :: (d -> a) -> (d -> b) -> (d -> b)
    next m w =  \ u -> let !_ = m u in w u
    
    instance Monad ((->) a) where
           .
           .
           .
        (>>)   = next
    
    main :: IO ()
    main =  putStr "ha" >> putStr "ha" >> putStr "!\n"
    
    main = let x = putStr "ha" in x >> x >> putStr "!\n"
    
    puts :: String -> (d -> ())
    putc :: Char -> (d -> ())
    
    main' :: d -> ()
    main' =  puts "ha" >> puts "ha" >> puts "!\n"
    
    main' = let x = puts "ha" in x >> x >> puts "!\n"
    
    let x = puts "ha" in x >> x
    
    let x = puts "ha" in \ u -> let !_ = x u in x u
    
    testme n = n^2 + n^2 + n
    
    testme n = let x = n^2 in x + x + n
    
    let x = puts "ha" in \ u -> let !u1:u2:_ = ... in
                                let !_ = x u1 in x u2
    
    bind     :: (d -> a) -> (a -> (d -> b)) -> (d -> b)
    bind m k =  \ u -> let !u1:u2:_ = ... in
                       let !x       = m u1 in
                       k x u2
    
    next     :: (d -> a) -> (d -> b) -> (d -> b)
    next m w =  \ u -> let !u1:u2:_ = ... in
                       let !_       = m u1 in
                       w u2
    
    let x = puts "ha" in \ u -> let !u1:u2:_ = ... in
                                let !_ = x u1 in x u
    
    data OI
    putstr   :: String -> OI -> ()
    putchar  :: Char -> OI -> ()
    
    getchar  :: OI -> Char
    
    next     :: (OI -> a) -> (IO -> b) -> OI -> b
    next m w =  \ u -> let !u1:u2:_ = ... in
                       let !_       = m u1 in
                       w u2
    
    next     :: (OI -> a) -> (IO -> b) -> OI -> b
    next m w =  \ u -> let !u1:u2:_ = parts u in
                       let !_       = m u1 in
                       w u2
    
    class Partible a where
        parts :: a -> [a]
    
    partsOI :: OI -> [OI]
    
    instance Partible OI where
        parts = partsOI
    
    putstr s =  \ u -> foldr (\!_ -> id) () $ zipWith putchar s $ parts u
    
    bind     :: (OI -> a) -> (a -> OI -> b) -> OI -> b
    bind m k =  \ u -> let !u1:u2:_ = parts u in
                       let !x       = m u1 in
                       k x u2
    
    unit     :: a -> OI -> a
    unit x   =  \ u -> x
    
    let x = puts "ha" in \ u -> let !u1:u2:_ = ... in
                                let !_ = x u1 in unit () u
    
    let x = puts "ha" in \ u -> let !u1:u2:_ = ... in
                                let !_ = x u1 in x u
    
    unit x   =  \ u -> let !_:_ = parts u in x
    
    part u  :: Partible a => a -> (a, a)
    part u  =  let !u1:u2:_ = parts u in (u1, u2)
    
    parts u =  let !(u1, u2) = part u in u1 : part u
    
    class Partible a where
        part  :: a -> (a, a)
        parts :: a -> [a]
    
         -- Minimal complete definition: part or parts
        part u  =  let !u1:u2:_ = parts u in (u1, u2)
        parts u =  let !(u1, u2) = part u in u1 : part u
    
    partOI :: OI -> (OI, OI)
    
    instance Partible OI where
        part = partOI
    
    unit     :: a -> OI -> a
    unit x   =  \ u -> let !(_, _) = part u in x
    
    bind     :: (OI -> a) -> (a -> OI -> b) -> OI -> b
    bind m k =  \ u -> let !(u1, u2) = part u in
                       let !x        = m u1 in
                       k x u2
    
    next     :: (OI -> a) -> (IO -> b) -> OI -> b
    next m w =  \ u -> let !(u1, u2) = part u in
                       let !_        = m u1 in
                       w u2
    
    main' :: OI -> ()
    
     -- the OI ADT:
    data OI                      
    putchar  :: Char -> OI -> ()
    getchar  :: OI -> Char
    partOI   :: OI -> (OI, OI)
    
    
    class Partible a where
        part  :: a -> (a, a)
        parts :: a -> [a]
    
         -- Minimal complete definition: part or parts
        part u  =  let !u1:u2:_ = parts u in (u1, u2)
        parts u =  let !(u1, u2) = part u in u1 : part u
    
    instance Partible OI where
        part = partOI
    
    
    putstr   :: String -> OI -> ()
    putstr s =  \ u -> foldr (\!_ -> id) () $ zipWith putchar s $ parts u
    
    unit     :: a -> OI -> a
    unit x   =  \ u -> let !(_, _) = part u in x
    
    bind     :: (OI -> a) -> (a -> OI -> b) -> OI -> b
    bind m k =  \ u -> let !(u1, u2) = part u in
                       let !x        = m u1 in
                       k x u2
    
    next     :: (OI -> a) -> (IO -> b) -> OI -> b
    next m w =  \ u -> let !(u1, u2) = part u in
                       let !_        = m u1 in
                       w u2
    
    instance Monad ((->) OI) where
        return = unit
        (>>=)  = bind
        (>>)   = next
    
    
    {- main' :: OI -> () -}
    
    newtype IO' a    =  IO' (FauxWorld -> (FauxWorld, a))
    
    data FauxWorld   =  FW OI
    
    instance Monad IO' where
        return x    =  IO' $ \ s@(FW _) -> (s, x) 
        IO' m >>= k =  IO' $ \ s@(FW _) -> let !(s', x) =  m s in
                                           let !(IO' w) =  k x in
                                           w s'
    
    putChar'         :: Char -> IO' ()
    putChar' c       =  IO' $ \ (FW u) -> let !(u1, u2) =  part u in
                                          let !_        =  putchar c u1 in
                                          (FW u2, ())
    
    putStr'          :: String -> IO' ()
    putStr' s        =  IO' $ \ (FW u) -> let !(u1, u2) =  part u in
                                          let !_        =  putstr s u1 in
                                          (FW u2, ())
    
    getChar'         :: IO' Char
    getChar'         =  IO' $ \ (FW u) -> let !(u1, u2) =  part u in
                                          let !c        =  getchar u1 in
                                          (FW u2, c)