Haskell:新类型的Functor实例 [英] Haskell: Functor instance for newtype

查看:46
本文介绍了Haskell:新类型的Functor实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

要设置水平,请使用简单的函子:

To level set, here's a simple functor:

data Box a = Box a deriving (Show)

instance Functor Box where
  fmap f (Box x) = Box (f x)

这使我们可以在盒子内"进行操作:

This allows us to operate "inside the box":

> fmap succ (Box 1)
Box 2

如何使用新类型实现相同的语法便利?假设我具有以下条件:

How do I achieve this same syntactic convenience with a newtype? Let's say I have the following:

newtype Width  = Width  { unWidth  :: Int } deriving (Show)
newtype Height = Height { unHeight :: Int } deriving (Show)

这有点笨拙:

> Width $ succ $ unWidth (Width 100)
Width {unWidth = 101}

这很好:

> fmap succ (Width 100)   -- impossible?
Width {unWidth = 101}

当然,我不能将Width或Height设为Functor的实例,因为它们都不具有种类* -> *.尽管从语法上讲它们与Box没有什么不同,所以似乎可以在不进行所有手动包装和拆包的情况下对基础值进行操作.

Of course, I can't make Width or Height an instance of a Functor since neither has kind * -> *. Although, syntactically they feel no different than Box, and so it seems like it should be possible to operate on the underlying value without all of the manual wrapping and unwrapping.

此外,由于每个新newtype的重复,创建这样的n个函数也不令人满意:

Also, it isn't satisfying to create n functions like this because of the repetition with every new newtype:

fmapWidth  :: (Int -> Int) -> Width  -> Width
fmapHeight :: (Int -> Int) -> Height -> Height

如何将Ints上的函数提升为Widths上的函数?

How do I lift a function on Ints to be a function on Widths?

推荐答案

首先请注意,newtype在这里并不困难-您可以像data一样对它们进行参数化,然后您就拥有了一个普通的仿函数.喜欢

First note that newtype is no hurdle here – you can parameterise these just as well as data, and then you have an ordinary functor. Like

{-# LANGUAGE DeriveFunctor #-}
newtype WidthF a = Width  { unWidth  :: a } deriving (Show, Functor)
type Width = WidthF Int

不过,我认为这不是一个好主意. Width 不应该是函子;在其中存储非数字类型没有任何意义.

I wouldn't consider that a good idea, though. Width shouldn't be a functor; it doesn't make sense to store non-number types in it.

user2407038建议的一个选项是使其成为单态函子"

One option as user2407038 suggests is to make it a "monomorphic functor"

import Data.MonoTraversable (MonoFunctor(..))

newtype Width = Width  { unWidth  :: Int } deriving (Show)

instance MonoFunctor Width where
  omap f (Width w) = Width $ f w

这对我来说似乎也不明智-如果您以一种通用方式映射数字运算,那么您最好为Num等提供Width实例并使用这些直接.但是,那么,您几乎没有比简单的类型系统更好的保证了

That too doesn't seem sensible to me – if you map number operations thus in one generic way, then you might just as well give Width instances for Num etc. and use these directly. But then you hardly have better type-system guarantees than a simple

type Width = Int

可以很容易地在没有任何帮助的情况下进行修改 ,另一方面,它很容易在没有任何类型的系统保护措施的情况下被错误地处理.

which can easily be modified without any help, the flip side being that it can easily be mishandled without any type system safeguards.

相反,我认为您想要的可能是这样:

Instead, I think what you want is probably this:

import Control.Lens

data Box = Box {
   width, height :: Int }

widthInPx, heightInPx :: Lens' Box Int
widthInPx f (Box w h) = (`Box`h) <$> f w
heightInPx f (Box w h) = (Box w) <$> f h

那你就可以做

> Box 3 4 & widthInPx %~ (*2)
Box 6 4
> Box 4 2 & heightInPx %~ succ
Box 4 3

这篇关于Haskell:新类型的Functor实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆