在`newtype a`上使用`a`的函数

Kev*_*ith 8 haskell newtype

假设我有以下内容newtype:

newtype Foo = Foo Integer deriving (Eq, Show)

有没有简洁的方法来添加两个Foo:

(Foo 10) + (Foo 5) == Foo 15

或获得最大值:

max (Foo 10) (Foo 5) == Foo 5

我很好奇是否可以轻松使用afor的功能newtype a而不是:

addFoo :: Foo -> Foo -> Foo
addFoo (Foo x) (Foo y) = Foo $ x + y
Run Code Online (Sandbox Code Playgroud)

jbe*_*man 12

就像haskell98知道如何为你派生那些EqShow实例一样,你可以打开GeneralizedNewtypeDerivingghc 的扩展来获得你需要的NumOrd实例:

Prelude> :set -XGeneralizedNewtypeDeriving 
Prelude> newtype Foo = Foo Integer deriving (Eq, Show, Num, Ord)
Prelude> (Foo 10) + (Foo 5) == Foo 15
True
Prelude> max (Foo 10) (Foo 5) == Foo 5
False
Run Code Online (Sandbox Code Playgroud)

  • @KevinMeredith是的,这是惯用的.但只是为了你自己的启蒙,你应该尝试自己编写`Ord`和`Num`实例. (3认同)

Aad*_*hah 10

您想要将类型的函数提升Integer -> Integer -> IntegerFoo -> Foo -> Foo.为此,您可以定义实用程序功能:

liftFoo :: (Integer -> Integer) -> Foo -> Foo
liftFoo f (Foo a) = Foo $ f a

liftFoo2 :: (Integer -> Integer -> Integer) -> Foo -> Foo -> Foo
liftFoo2 f (Foo a) (Foo b) = Foo $ f a b

-- and so on
Run Code Online (Sandbox Code Playgroud)

然后你可以使用它如下:

liftFoo2 (+) (Foo 10) (Foo 5)

liftFoo2 max (Foo 10) (Foo 5)
Run Code Online (Sandbox Code Playgroud)

这具有不需要扩展的优点.


另一种选择是使定义FooNEWTYPE更容许这样你可以把它的实例FunctorApplicative:

import Control.Applicative

newtype Foo a = Foo a deriving (Eq, Show)

foo :: Integer -> Foo Integer
foo = Foo

instance Functor Foo where
    fmap f (Foo a) = Foo $ f a

instance Applicative Foo where
    pure = Foo
    (Foo f) <*> (Foo a) = Foo $ f a
Run Code Online (Sandbox Code Playgroud)

现在您可以执行以下操作:

(+) <$> foo 10 <*> foo 5

max <$> foo 10 <*> foo 5
Run Code Online (Sandbox Code Playgroud)

因为foo是专门的Integer类型,你不会失去任何类型检查的好处.