haskellderivingderivingvia# Deriving newtype recursively like functionality

Lets say, in package `P`

, I've got a type `A`

, defined something like so:

```
newtype A = A Int
```

There's then external package `X`

(which I don't control, unlike `P`

and `Q`

), which has a class `C`

and an instance:

```
instance C Int where ...
```

So `X`

depends on `base`

.

Note `P`

doesn't depend on `X`

.

Now there's a package `Q`

, which has a type `B`

like so:

```
newtype B = B A
```

I want the following:

```
instance C B where ...
```

One way of achieving this is just adding deriving newtype clauses like so:

```
newtype A = A Int deriving newtype C -- in package P
newtype B = B A deriving newtype C -- in package Q
```

But then package `P`

must depend on package `X`

, all because `Q`

want to use it in a particular way.

This is what I've been doing, but I feel like my codebase is becoming a bit of a blob. As in everything depends on everything. In somecases I've just been eliminating the newtype `B`

entirely. But I think what is happening is my abstract utility code is becoming increasingly coupled with particular business logic, and I don't think this is a good thing.

I guess there's this approach also:

```
newtype B = B Int deriving newtype C -- in package Q
```

But then if the implementation of `A`

changes, say like so:

```
newtype A = A Integer -- in package P
```

Then I've broken my code, as presumably what I'd now be doing to create an `A`

is unwrapping `B`

and wrapping it up in `A`

.

I guess I could do this:

```
type A' = Int
newtype A = A A' -- in package P
newtype B = B A' deriving newtype C -- in package Q
```

But now things are getting a bit convoluted.

So I'm looking for alternatives to decouple `P`

and `Q`

, and in particular, not require `P`

to depend on `X`

(the package that defines `C`

).

I'd like something like:

```
deriving newtype recursively C
```

Which pokes down unwrapping newtypes until it finds one that implements `C`

, but I'm not sure it exists, and it probably isn't a good idea in the general case anyway (one sometimes newtypes to particularly hide certain instances that don't make sense, one does not want `Num`

defined on timestamps, even if they are integers underneath).

So here I'm a bit stuck, and not sure the best way forward. Just looking for some options because this seems like a situation others would have encountered also.

Solution

Well, you can use the `DerivingVia`

extension to write:

```
newtype B = B A deriving C via Int
```

That is, the contents of your modules can be:

```
-- X.hs
module X where
class C a where
c :: a -> String
instance C Int where
c _ = "Int"
-- P.hs
module P where
newtype A = A Int
-- Q.hs
{-# LANGUAGE DerivingVia #-}
module Q where
import X
import P
newtype B = B A deriving C via Int
main = print $ c (B (A 10))
```

where Q depends on X and P, but P need not depend on X.

If the representation of `A`

changes, this will result in a compile time error which will require you to update the `via`

clause, but I'm not sure how you could change the underlying representation of `A`

and not expect to have to fix some usages of `B`

in module Q.

- 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