On Thu, Jan 22, 2026 at 11:20:28PM +0100, Andreas Klebinger via Haskell-Cafe wrote:
(I don't think what Andreas said can *literally* be correct, because surely it's safe to unsafeCoerce from a to b when `Coercible a b` is in scope?)
Yes I was perhaps a bit too restrictive. The docs in fact explicitly mention this case among some others. I don't see how it could go wrong here. In fact the docs mention this case in particular! (https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Unsafe-Coerce...):
(superseded) Casting between two types which have exactly the same structure: between a newtype of T and T, or between types which differ only in "phantom" type parameters. It is now preferred to use |coerce https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Coerce.h...| from |Data.Coerce|, which is type-safe.
But really there is no reason to use dirty tricks like unsafeCoerce in this situation to begin with when `coerce` is right there!
Yes, agreed. My objection was a bit of a technicality, but since I'm in unfamiliar and dangerous territory technicalities are all I have to hold on to.
-----------------
The concrete example sadly doesn't seem to compile.
Ah, you're right. Here's a version which does compile. {-# LANGUAGE QuantifiedConstraints #-} import GHC.Exts (Any) import Data.Kind (Type) import Unsafe.Coerce (unsafeCoerce) data C (f :: Type -> Type -> Type) where MkC :: (forall a. Monad (f a)) => C f newtype CD (f :: Type -> Type -> Type) = MkCD (C Any) putCD :: C f -> CD f putCD c = MkCD (unsafeCoerce c) getCD :: CD f -> C f getCD (MkCD cd) = unsafeCoerce cd
And type level edge cases are really not my speciality so maybe someone else can make more definite statements there.
OK, thanks very much for taking a look! Tom