【问题标题】:Function with type a -> b in Haskell?Haskell中具有a-> b类型的函数?
【发布时间】:2016-09-25 00:26:53
【问题描述】:

Haskell 中是否有类型为a -> b 的函数?这意味着,是否可以编写一个函数,例如f :: a -> b?我认为不存在这样的函数,原因如下:假设我们在f :: a -> b 的位置找到了f,那么f 2 会产生什么? b 类型的值,但是 b 是什么,因为 Haskell 无法从我给出的参数中推断(我认为)它?它是否正确?否则,你能给我一个这样的功能的例子吗?

【问题讨论】:

  • 你说得对,很难写出这种类型的有趣函数;但您给出的reason 不正确。当然允许编写在以某些方式应用时导致类型不明确的函数。
  • 任何在解释为逻辑公式时不是重言式的类型都是偏函数的类型,它必须永远抛出错误或循环,至少对于某些输入。 forall a b. a -> b 不是重言式,因为你可以很容易地想到反例:例如Int -> Void——仅仅因为你给了我一个Int,并不意味着我可以给你一个Voidhead :: [a] -> a 也是如此。你可以通过查看类型来判断,如果你给它一个空列表[],它就不能返回结果。

标签: haskell types type-inference unification


【解决方案1】:

为了实现f :: a -> b,这意味着f 必须能够返回任何可能的类型。即使是今天不存在的类型,但有人可以在十年后定义。如果没有某种反射功能,这显然是不可能的。

嗯...“不可能”是一个大词...正如其他答案指出的那样,排除底部是不可能的。换句话说,f 永远不能返回 b 类型的值。它可以抛出异常,或者永远循环。但是(可以说)这些东西都不是真正的“返回值”。

f1 :: a -> b
f1 = error "f1"

f2 :: a -> b
f2 s = error "f2"

f3 :: a -> b
f3 x = f3 x

这些函数都有细微的不同,它们都编译得很好。当然,它们都是无用的!所以是的,没有类型为a -> b有用函数。

如果你想分头发:

  • f1 抛出异常。
  • f1 'x' 抛出异常。
  • f2 是一个看起来很普通的函数。
  • f2 'x' 抛出异常。
  • f3 是一个看起来很普通的函数。
  • f3 'x' 不会抛出异常,但它会永远循环,所以它实际上从不返回任何东西。

基本上,您看到的任何返回“任何类型”的函数都是一个从未真正返回的函数。我们可以在不寻常的单子中看到这一点。例如:

f4 :: a -> Maybe b

完全有可能在不抛出异常或永远循环的情况下实现此功能。

f4 x = Nothing

同样,我们实际上并没有返回b。我们也可以这样做

f5 :: a -> [b]
f5 x = []

f6 :: a -> Either String b
f6 x = Left "Not here"

f7 :: a -> Parser b
f7 x = fail "Not here"

【讨论】:

    【解决方案2】:

    除了 ⊥(底值undefined 等),这总是可能的,但永远不会有用,确实不可能有这样的功能。这是我们从多态类型签名中获得的所谓free theorems 的最简单实例之一。

    您对为什么这是不可能的直观解释走在正确的轨道上,尽管它最终还是失败了。是的,你可以考虑f (5 :: Int)。问题是不是编译器“无法推断”b 会是什么——许多现实函数都会出现这种情况,例如

    fromIntegral :: (Num b, Integral a) => a -> b
    

    说得通; b 将从使用fromIntegral x 的环境中推断出。例如,我可能会写

    average :: [Double] -> Double
    average l = sum l / fromIntegral (length l)
    

    在这种情况下,length l :: a 具有固定类型 Int 并且 fromIntegral (length l) :: b 必须具有固定类型 Double 以适应环境,并且与大多数其他具有类型推断的语言不同,来自环境的信息是在此以基于 Hindley-Milner 的语言提供。

    不,f :: a -> b 的问题在于您可以将ab 实例化为任何荒谬的类型组合,而不仅仅是不同的数字类型。因为f 是不受约束的多态性,它必须能够将任何类型转换为任何其他类型

    特别是它可以转换成真空类型Void

    evil :: Int -> Void
    evil = f
    

    然后我可以拥有

    muahar :: Void
    muahar = f 0
    

    但是,通过Void 的构造,不能存在这种类型的值(除了 ⊥ 之外,您无法在不崩溃或无限循环的情况下进行评估)。


    应该注意,按照某些标准,这并不是计算平均值的好方法。

    【讨论】:

      【解决方案3】:

      ...但是b 是什么,因为 Haskell 无法从我给出的论点中推断出来?

      根据上下文,Haskell 可以推断返回类型;说:

      {-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}
      
      class Cast a b where
          cast :: a -> b
      
      instance Cast a a where
          cast = id
      
      instance Cast Int String where
          cast = show
      
      instance Cast Int Double where
          cast = fromIntegral
      

      那么,

      cast :: Cast a b => a -> b
      

      如果给定足够的上下文,Haskell 知道要使用哪个函数:

      \> let a = 42 :: Int
      \> let b = 100.0 :: Double
      
      \> "string: " ++ cast a  -- Int -> String
      "string: 42"
      
      \> b * cast a            -- Int -> Double
      4200.0
      

      【讨论】:

      • 添加类型约束改变了问题,但是,以同样的方式询问有多少函数具有类型a -> a 取决于是否存在约束。例如ida -> a类型的唯一函数,但Num a => a -> a类型的函数有很多。
      【解决方案4】:

      我认为确实有一个,但它有点作弊:

      > let f _ = undefined
      > :t f
      f:: t -> t1
      

      这只是因为底部被认为是每种类型的值。

      【讨论】:

      • 我认为这根本不是作弊。如果您要返回一个未知/未指定类型的值,我想不出除了底部之外您可能返回的任何其他值。
      • “正好一个”可能有点强。如果您的意思是语义上,那么还有其他选项,例如 f = undefinedf x = seq x undefined 在实际方面并没有真正的不同,如果您的意思是实际上,那么还有其他选项,例如 f x = f x (即那个循环而不是抛出一个例外)在语义上并没有真正不同。当然还有一个明显的警告,它在语义和实践上都生活在一个陌生的空间中......
      • @DanielWagner 确切地说是一个 pure 函数是否公平?
      • @chepner 是什么让f = undefinedf x = f x 的纯度不如f _ = undefined
      • 我的意思是f x = seq x undefined;只看输入和输出,和f x = undefined一样。至于f = undefined,如果你假设f :: a -> b,那和f _ = undefined不一样吗? (唯一的区别是前者有undefined :: a -> b,后者有undefined :: b。)我不太了解undefined 的确切语义,不能很好地说明f x = f x 是否与f _ = undefined.
      猜你喜欢
      • 2020-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-03
      • 2017-04-19
      • 1970-01-01
      相关资源
      最近更新 更多