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

时间:2014-10-06 02:35:39

标签: haskell newtype

我们说我有以下newtype

newtype Foo = Foo Integer deriving (Eq, Show)

是否有简洁的方法可以添加两个Foo&#39>:

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

或获取最大值:

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

我很好奇是否可以轻松地将a的功能用于newtype a而不是:

addFoo :: Foo -> Foo -> Foo
addFoo (Foo x) (Foo y) = Foo $ x + y

5 个答案:

答案 0 :(得分:12)

正如haskell98知道如何为您推导EqShow个实例一样,您可以启用GeneralizedNewtypeDeriving扩展名为ghc来获取Num和{{ 1}}你需要的实例:

Ord

答案 1 :(得分:10)

您想要将Integer -> Integer -> Integer类型的功能提升为Foo -> 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

然后您可以按如下方式使用它:

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

liftFoo2 max (Foo 10) (Foo 5)

这样做的好处是不需要扩展。


另一种选择是让Foo newtype的定义更加允许,以便您可以将其设为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

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

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

max <$> foo 10 <*> foo 5

由于foo专门针对Integer类型,因此您不会失去任何类型检查的好处。

答案 2 :(得分:3)

您也可以使用安全强制。大致您使用Data.Coerce.coerce自动换行/解包新类型。

> import Data.Coerce
> newtype Foo = Foo Integer deriving (Eq, Show, Ord)
> coerce (Foo 1) :: Integer
1
> let f :: Integer -> Integer ; f x = x + 1
> coerce f (Foo 10)
Foo 11
>  coerce (succ :: Integer -> Integer) (Foo 10) :: Foo
Foo 11
> coerce (max :: Integer -> Integer -> Integer) (Foo 10) (Foo 5) :: Foo
Foo 10

请注意,它对于f的单态函数非常有用,但对于succ这样的多态函数则不太适用,因为在后一种情况下需要使用类型注释。

答案 3 :(得分:2)

要获得数学运算,您需要使Foo成为Num类型类的实例。这意味着您必须定义(+)(*)abssignumfromInteger以及negate或{{1} } (-)。一旦定义了这些函数,您将获得免费在Foo上运行的其他函数。

要让Num和类似功能发挥作用,您需要将max设为Foo的实例。这需要定义Ordcompare

通常,您可以在ghci中使用(<=)来查找函数的类型,其中包括与其一起使用的类型类。然后,您只需确定必须为该类型类定义的最小函数集。

答案 4 :(得分:0)

有几个库提供组合器,用于将基础类型上的功能提升为新类型。

对于您给出的示例,可以使用newtype-generics中类型为{p>的over2组合器

over2 :: (Newtype n, Newtype n', o' ~ O n', o ~ O n) => (o -> n) -> (o -> o -> o') -> n -> n -> n'

或来自coercible-utils类型的

over2 :: (Coercible a b, Coercible a' b') => (a -> b) -> (a -> a -> a') -> b -> b -> b'

用法(对于coercible-utils中的版本)是这样的:

λ over2 Foo (+) (Foo 10) (Foo 5) :: Foo
Foo 15