【问题标题】:Haskell: Functor instance for newtypeHaskell:新类型的函子实例
【发布时间】:2019-04-22 22:41:45
【问题描述】:

为了水平集,这里有一个简单的函子:

data Box a = Box a deriving (Show)

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

这让我们可以“在盒子里”操作:

> fmap succ (Box 1)
Box 2

如何使用新类型实现同样的语法便利?假设我有以下内容:

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 没有什么不同,因此看起来应该可以在没有所有手动包装和展开的情况下对底层值进行操作。

另外,创建 n 个这样的函数并不令人满意,因为每个新类型都会重复:

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

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

【问题讨论】:

  • 你想要一个different
  • 这样做没有意义:仿函数是高阶类型。
  • 第三个选项是来自 lens(或类似软件包)的over:为您的领域提供一个名为 unWidth 的镜头,over unWidth 可以满足您的需求(即它可以让你使用单态函子)。
  • 问题不在于新类型。说data Widthnewtype Box a 并没有任何变化(除了这里不重要的懒惰)。
  • @n.m.明白了。我试图为这个问题命名以匹配未来的搜索。我预计不会有任何新的 Haskell 用户搜索“如何为单态容器编写函子”。关于如何修改标题或内容以实现 (1) 准确性和 (2) 可搜索性的任何建议?

标签: haskell


【解决方案1】:

首先请注意,newtype 在这里没有障碍——您可以像 data 一样对这些参数进行参数化,然后您就有了一个普通的函子。喜欢

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

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

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

import Data.MonoTraversable (MonoFunctor(..))

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

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

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

type Width = Int

无需任何帮助即可轻松修改,另一方面是在没有任何类型系统保护措施的情况下很容易被错误处理。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2016-08-12
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-06
    相关资源
    最近更新 更多