Haskell 是否可以在现有类型的子集上定义函数?

Haskell 是否可以在现有类型的子集上定义函数?,haskell,Haskell,我是Haskell的新手,我想知道是否可以定义一个只在现有类型的子集上定义的函数,而不需要定义新类型 示例:我想创建一个只接受偶数整数(或偶数自然数等)并返回该数平方的函数,如: squared :: 2*Integer -> Integer squared n = n*n 当然,上述两条线不起作用 我知道我可以这样写: squared' :: Integer -> Integer squared' n | (even n) = n*n | otherwise = err

我是Haskell的新手,我想知道是否可以定义一个只在现有类型的子集上定义的函数,而不需要定义新类型

示例:我想创建一个只接受偶数整数(或偶数自然数等)并返回该数平方的函数,如:

squared :: 2*Integer -> Integer
squared n = n*n
当然,上述两条线不起作用

我知道我可以这样写:

squared' :: Integer -> Integer
squared' n 
  | (even n) = n*n
  | otherwise = error "n is not even!"
或者类似的东西,但我想知道像非工作示例这样的东西是否也可能


我希望这个问题不是完全愚蠢的(或者已经被回答了),但我真的不太了解Haskell(所以寻找答案也有点困难)

通常不会。这样的东西叫做子集类型,它是Haskell没有的依赖类型的一个标志。通常它是通过将一个值与一个值满足某些属性的证明装箱来实现的,但是由于我们在Haskell中没有证明的概念,所以我们被卡住了

通常,伪造它的方法是使用“智能构造函数”

然后隐藏
偶数
构造函数


如果你真的想要它,你可以切换到一种可以与Haskell进行互操作的语言,这种语言具有依赖类型(请记住Coq和Agda spring)。

否。类型系统需要支持细化类型(或完全依赖类型,如@jozefg所建议)

这是一个带有细化类型的Haskell扩展


将子集包装为新类型

newtype EvenInteger = EvenInteger {
    unEvenInteger :: Integer
} deriving (Show, Eq, Ord, Num)

mkEvenInteger :: Integer -> Maybe EvenInteger
mkEvenInteger n = case n % 2 of
    0 -> Just $ EvenInteger n
    _ -> Nothing

squared :: EvenInteger -> EvenInteger
squared n = n * n

一种可能是

newtype Even n = Even n
getEven (Even n) = 2*n

squared :: Num n => Even n -> Even n
squared (Even n) = Even (2*n*n)

正如其他地方提到的,LiquidHaskell中的细化类型可以表达这一点。下面是它的样子:

module Evens where

{-@ type Even = {v:Int | v mod 2 = 0} @-}

{-@ square :: Even -> Int @-}
square :: Int -> Int
square n = n * n

-- calling the function:
yup = square 4
-- nope = square 3 -- will not compile if this is uncommented

您可以将其插入此处进行尝试:

对于依赖类型,在Haskell Hooray中并不容易!如果依赖类型的概念有点混乱,请查看以下视频:。它提供了一个很好的例子,使用依赖类型来提供额外的信息,如列表的长度等。所有这些都在类型级别。这不起作用,
1::EvenInteger
将1注入偶数域。。。你说得对。如果你愿意处理语法上的丑陋,这个解决方案可以在不派生
Num
的情况下工作。不过,在这种情况下,我认为治疗比疾病更糟糕:(
module Evens where

{-@ type Even = {v:Int | v mod 2 = 0} @-}

{-@ square :: Even -> Int @-}
square :: Int -> Int
square n = n * n

-- calling the function:
yup = square 4
-- nope = square 3 -- will not compile if this is uncommented