che*_*ter 3 haskell deriving derivingvia
我希望能够为包含多个字段的 ADT派生Eq和派生Show。其中之一是功能字段。这样做时Show,我希望它显示一些虚假的东西,例如"<function>"; 这样做时Eq,我希望它忽略该字段。我怎样才能最好地做到这一点,而无需为Showand手写一个完整的实例Eq?
我不想将函数字段包装在 a 中并为此newtype编写我自己的字段- 那样使用太麻烦了。EqShow
获得正确Eq和Show实例的一种方法是,不要对该函数字段进行硬编码,而是将其设为类型参数并提供一个仅“擦除”该字段的函数。也就是说,如果你有
data Foo = Foo
{ fooI :: Int
, fooF :: Int -> Int }
Run Code Online (Sandbox Code Playgroud)
你把它改成
data Foo' f = Foo
{ _fooI :: Int
, _fooF :: f }
deriving (Eq, Show)
type Foo = Foo' (Int -> Int)
eraseFn :: Foo -> Foo' ()
eraseFn foo = foo{ fooF = () }
Run Code Online (Sandbox Code Playgroud)
然后,Foo仍然不会Eq- 或Show能够(毕竟它不应该是),但是要使Foo值可显示,您可以将其包装在eraseFn.
通常,我在这种情况下做的正是你说你不想要做的,即在包装的功能newtype,并提供了一个Show为:
data T1
{ f :: X -> Y
, xs :: [String]
, ys :: [Bool]
}
Run Code Online (Sandbox Code Playgroud)
data T2
{ f :: OpaqueFunction X Y
, xs :: [String]
, ys :: [Bool]
}
deriving (Show)
newtype OpaqueFunction a b = OpaqueFunction (a -> b)
instance Show (OpaqueFunction a b) where
show = const "<function>"
Run Code Online (Sandbox Code Playgroud)
如果您不想这样做,您可以将函数设为类型参数,并在Show输入类型时将其替换掉:
data T3' a
{ f :: a
, xs :: [String]
, ys :: [Bool]
}
deriving (Functor, Show)
newtype T3 = T3 (T3' (X -> Y))
data Opaque = Opaque
instance Show Opaque where
show = const "..."
instance Show T3 where
show (T3 t) = show (Opaque <$ t)
Run Code Online (Sandbox Code Playgroud)
或者我将重构我的数据类型以Show仅派生我希望Show默认使用的部分,并覆盖其他部分:
data T4 = T4
{ f :: X -> Y
, xys :: T4' -- Move the other fields into another type.
}
instance Show T4 where
show (T4 f xys) = "T4 <function> " <> show xys
data T4' = T4'
{ xs :: [String]
, ys :: [Bool]
}
deriving (Show) -- Derive ‘Show’ for the showable fields.
Run Code Online (Sandbox Code Playgroud)
或者,如果我的类型很小,我将使用 anewtype而不是data,并Show通过以下方式派生OpaqueFunction:
{-# LANGUAGE DerivingVia #-}
newtype T5 = T5 (X -> Y, [String], [Bool])
deriving (Show) via (OpaqueFunction X Y, [String], [Bool])
Run Code Online (Sandbox Code Playgroud)
如果您关心保留字段名称/记录访问器,您可以使用该iso-deriving包为data使用镜头的类型执行此操作。
至于Eq(or Ord),让一个实例等同于可以以某种方式明显区分的值并不是一个好主意,因为某些代码会将它们视为相同而其他代码则不会,现在您不得不关心稳定性:在某些情况下,我a == b应该选择a还是b?这就是为什么可替代性是一个定律Eq:forall x y f. (x == y) ==> (f x == f y)iff是一个“公共”函数,它支持xand类型的不变量y(尽管浮点数也违反了这一点)。更好的选择是类似T4上面的内容,仅对可以满足法律的类型的部分具有相等性,或者在使用站点显式使用比较模某个函数,例如,comparing someField.
| 归档时间: |
|
| 查看次数: |
121 次 |
| 最近记录: |