I have a type that looks like this:
newtype Canonical Int = Canonical Int
and a function
canonicalize :: Int -> Canonical Int
canonicalize = Canonical . (`mod` 10) -- or whatever
(The Canonical type may not be important, it just serves to distinguish "raw" values from "canonicalized" values.)
I'd like to create some machinery so that I can canonicalize the results of function applications.
For example: (Edit: fixed bogus definitions)
cmap :: (b->Int) -> (Canonical b) -> (Canonical Int)
cmap f (Canonical x) = canonicalize $ f x
cmap2 :: (b->c->Int) -> (Canonical b) -> (Canonical c) -> (Canonical Int)
cmap2 f (Canonical x) (Canonical y) = canonicalize $ f x y
That's superficially similar to Functor and Applicative, but it isn't quite, because it's too specialized: I can't actually compose functions (as required by the homomorphism laws for Functor/Applicative) unless 'b' is Int.
My goal is to use existing library functions/combinators, instead of writing my own variants like cmap
, cmap2
. Is that possible? Is there a different typeclass, or a different way to structure Canonical type, to enable my goal?
I've tried other structures, like
newtype Canonical a = Canonical { value :: a, canonicalizer :: a -> a }
but that hits the same non-composability problem, because I can't translate one canonicalizer to another (I just want to use the canonicalizer of the result type, which is always Int
(or Integral a
)
And I can't force "specialization-only" like so, this isn't valid Haskell:
instance (Functor Int) (Canonical Int)
(and similar variations)
I also tried
newtype (Integral a) => Canonical a = Canonical a -- -XDatatypeContexts
instance (Integral a) => Functor Canonical where
fmap f (Canonical x) = canonicalize $ f x
but GHC says that DatatypeContexts
is deprecated, and a bad idea, and more severely,
I get:
`Could not deduce (Integral a1) arising from a use of 'C'
from the context (Integral a)
bound by the instance declaration
[...] fmap :: (a1 -> b) -> (C a1 -> C b)
which I think is saying that the constraint Integral a
can't actually be used to constrain fmap
to (Integral -> Integral)
the way I wish, which is sort of obvious (since fmap
has two type variables) :-(
And of course this isn't valid Haskell either
instance (Integer a) => Functor Canonical where
Is there a similar typeclass I could use, or am I wrong to try to use a typeclass at all for this functionality of "implicitly canonicalize the results of function calls"?
I think what you're trying to achieve is available in the mono-traversable package, and in this case the MonoFunctor typeclass.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With