haskellderivingderivingvia# Deriving Eq and Show for an ADT that contains fields that can't have Eq or Show

I'd like to be able to derive `Eq`

and `Show`

for an ADT that contains multiple fields. One of them is a function field. When doing `Show`

, I'd like it to display something bogus, like e.g. `"<function>"`

; when doing `Eq`

, I'd like it to ignore that field. How can I best do this without hand-writing a full instance for `Show`

and `Eq`

?

I don't want to wrap the function field inside a `newtype`

and write my own `Eq`

and `Show`

for that - it would be too bothersome to use like that.

Solution

Typically what I do in this circumstance is exactly what you say you *don’t* want to do, namely, wrap the function in a `newtype`

and provide a `Show`

for that:

```
data T1
{ f :: X -> Y
, xs :: [String]
, ys :: [Bool]
}
```

```
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>"
```

If you don’t want to do that, you can instead make the function a type parameter, and substitute it out when `Show`

ing the type:

```
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)
```

Or I’ll refactor my data type to derive `Show`

only for the parts I want to be `Show`

able by default, and override the other parts:

```
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.
```

Or if my type is small, I’ll use a `newtype`

instead of `data`

, and derive `Show`

via something like `OpaqueFunction`

:

```
{-# LANGUAGE DerivingVia #-}
newtype T5 = T5 (X -> Y, [String], [Bool])
deriving (Show) via (OpaqueFunction X Y, [String], [Bool])
```

You can use the `iso-deriving`

package to do this for `data`

types using lenses if you care about keeping the field names / record accessors.

As for `Eq`

(or `Ord`

), it’s not a good idea to have an instance that equates values that can be observably distinguished in some way, since some code will treat them as identical and other code will not, and now you’re forced to care about *stability*: in some circumstance where I have `a == b`

, should I pick `a`

or `b`

? This is why *substitutability* is a law for `Eq`

: `forall x y f. (x == y) ==> (f x == f y)`

if `f`

is a “public” function that upholds the invariants of the type of `x`

and `y`

(although floating-point also violates this). A better choice is something like `T4`

above, having equality only for the parts of a type that can satisfy the laws, or explicitly using comparison modulo some function at use sites, e.g., `comparing someField`

.

- Comparing lists in Haskell
- Is there a non-identity monad morphism M ~> M that is monadically natural in M?
- Problem with loading module ‘Distribution.Simple’
- Improving efficiency in Stirling numbers calculation
- Does sequencing an infinite list of IO actions by definition result in a never-ending action? Or is there a way to bail out?
- How to call pgQuery from postgresql-query?
- How to avoid whitespace after a tag (link) in Hamlet templates?
- Understanding type-directed resolution in Haskell with existential types
- Why is seq bad?
- Understanding bind function in Haskell
- How to create route that will trigger on any path in Servant?
- How do I use a global state in WAI middleware?
- nixos 23.11 cabal install mysql-simple problem - "Missing (or bad) C libraries"
- Is there a way to kill all forked threads in a GHCi session without restarting it?
- Why can an invalid list expression such as 2:1 be assigned to a variable, but not printed?
- Iterate over a type level list and call a function based on each type in the list
- How does this solution of Project Euler Problem 27 in the Haskell Wiki work?
- Why `Monad` is required to use `pure`?
- Can't do partial function definitions in GHCi
- recommended way to convert Double -> Float in Haskell
- Haskell profiling understanding cost centre summary for anonymous lambda
- Why is Haskell fully declarative?
- GHC Generating Redundant Core Operations
- Question about Event firing in reflex-frp
- Using Haskell's "Maybe", type declarations
- How can I elegantly invert a Map's keys and values?
- Why there is no output for wrapped IO in Haskell?
- What are the definitions of Weather and Memory in xmobar repo?
- Serializing a Data.Text value to a ByteString without unnecessary \NUL bytes
- Using Haskell with VS Code