代码之家  ›  专栏  ›  技术社区  ›  acontell

使用Church数字的某些操作的类型签名声明

  •  2
  • acontell  · 技术社区  · 7 年前

    -- Church numerals in Haskell.
    type Numeral a = (a -> a) -> (a -> a)
    
    churchSucc :: Numeral a -> Numeral a
    churchSucc n f = \x -> f (n f x)
    
    -- Operations with Church numerals.
    sum :: Numeral a -> Numeral a -> Numeral a
    sum m n = m . churchSucc n
    
    mult :: Numeral a -> Numeral a -> Numeral a
    mult n m = n . m
    
    -- Here comes the first problem
    -- exp :: Numeral a -> Numeral a -> Numeral a
    exp n m = m n
    
    -- Convenience function to "numerify" a Church numeral.
    add1 :: Integer -> Integer
    add1 = (1 +)
    
    numerify :: Numeral Integer -> Integer
    numerify n = n add1 0
    
    -- Here comes the second problem
    toNumeral :: Integer -> Numeral Integer
    toNumeral 0 = zero
    toNumeral (x + 1) = churchSucc (toNumeral x)
    

    我的问题来自幂运算。如果我声明类型签名 toNumeral exp 声带 经验 ?

    1 回复  |  直到 7 年前
        1
  •  6
  •   HTNW    7 年前

    exp 不能以你的方式书写,因为它需要通过一个 Numeral 作为a的参数 数字 . 这需要有一个 Numeral (a -> a) Numeral a . 你可以这样写

    exp :: Numeral a -> Numeral (a -> a) -> Numeral a
    exp n m = m n
    

    我看不出有什么问题 toNumeral 除了像这样的模式之外 x + 1 不应使用。

    toNumeral :: Integer -> Numeral a -- No need to restrict it to Integer
    toNumeral 0 = \f v -> v
    toNumeral x
      | x > 0 = churchSucc $ toNumeral $ x - 1
      | otherwise = error "negative argument"
    

    此外,您的 sum m . churchSucc n m * (n + 1) ,所以应该是:

    sum :: Numeral a -> Numeral a -> Numeral a
    sum m n f x = m f $ n f x -- Repeat f, n times on x, and then m more times.
    

    然而,教会数字是起作用的函数 类型。也就是说, Numeral String 不应与 Numeral Integer ,因为 通用量化 : 是一个函数,适用于所有类型 a , (a -> a) -> (a -> a) RankNTypes type Numeral = forall a. (a -> a) -> (a -> a) .

    这是有道理的:一个教会数字是由它的函数参数重复多少次来定义的。 \f v -> v 电话 f 0次,所以是0, \f v -> f v 等于1,等等。强制 数字 为所有人工作 确保它可以 只有 关心什么类型 f v have删除了限制,并允许您编写 (\f v -> "nope") :: Numeral String

    我会这样写

    {-# LANGUAGE RankNTypes #-}
    
    type Numeral = forall a. (a -> a) -> (a -> a)
    
    _0 :: Numeral
    _0 _ x = x
    -- The numerals can be defined inductively, with base case 0 and inductive step churchSucc
    -- Therefore, it helps to have a _0 constant lying around
    
    churchSucc :: Numeral -> Numeral
    churchSucc n f x = f (n f x) -- Cleaner without lambdas everywhere
    
    sum :: Numeral -> Numeral -> Numeral
    sum m n f x = m f $ n f x
    
    mult :: Numeral -> Numeral -> Numeral
    mult n m = n . m
    
    exp :: Numeral -> Numeral -> Numeral
    exp n m = m n
    
    numerify :: Numeral -> Integer
    numerify n = n (1 +) 0
    
    toNumeral :: Integer -> Numeral
    toNumeral 0 = _0
    toNumeral x
      | x > 0 = churchSucc $ toNumeral $ x - 1
      | otherwise = error "negative argument"
    

    演示:

    main = do out "5:" _5
              out "2:" _2
              out "0:" _0
              out "5^0:" $ exp _5 _0
              out "5 + 2:" $ sum _5 _2
              out "5 * 2:" $ mult _5 _2
              out "5^2:" $ exp _5 _2
              out "2^5:" $ exp _2 _5
              out "(0^2)^5:" $ exp (exp _0 _2) _5
           where _2 = toNumeral 2
                 _5 = toNumeral 5
                 out :: String -> Numeral -> IO () -- Needed to coax the inferencer
                 out str n = putStrLn $ str ++ "\t" ++ (show $ numerify n)