Description
I recognized that
mymap :: (a -> b) -> Vec n a -> Vec n b
mymap _ Nil = Nil
mymap f (x `Cons` xr) = f x `Cons` mymap f xr
type-checks, but
mymap' :: (a -> b) -> Vec n a -> Vec n b
mymap' _ Nil = Nil
mymap' f (x :> xr) = f x :> mymap' f xr
does not (GHC 9.10):
<interactive>:4:11: error: [GHC-25897]
• Couldn't match type ‘n’ with ‘n0 + 1’
Expected: Vec n a
Actual: Vec (n0 + 1) a
‘n’ is a rigid type variable bound by
the type signature for:
mymap' :: forall a b (n :: Nat). (a -> b) -> Vec n a -> Vec n b
at <interactive>:2:1-40
• In the pattern: x :> xr
In an equation for ‘mymap'’:
mymap' f (x :> xr) = f x :> mymap' f xr
• Relevant bindings include
mymap' :: (a -> b) -> Vec n a -> Vec n b
(bound at <interactive>:3:1)
This is kind of annoying, as up to my knowledge most people use (:>)
and are not much aware about Cons
. A little research revealed that there already was a discussion about this in the past (#170, #966, #867, #943), but that discussion was within the scope of some old GHC (we don't even support any more). Thus, it might be that these past considerations need some re-evaluation first.
My primary question would be: "Do we still need both: Cons
and (:>)
these days?"
A possible solution would be to update the (:>)
synonym to
pattern (:>) ::
forall n a. () =>
forall m. n ~ m + 1 =>
a -> Vec m a -> Vec n a
pattern (:>) x xs = Cons x xs
which elides the aforementioned type-mismatch (and yes, the explicit quantifier split is necessary here). However, this change also introduces a different quantifier alternation than Cons
, which might be non-desirable.
Another solution (my favorite) would be to just rename Cons
to (:>)
and to drop Cons
, as I could not find any counterexample, where this solution would cause any trouble, i.e., where using the constructor directly breaks things.