Mam następujący typ danych:twierdzą, że konstruktor danych jest injective
{-# LANGUAGE GADTs, KindSignatures, ScopedTypeVariables, DataKinds #-}
import GHC.TypeLits
import Unsafe.Coerce
data Var (i :: Nat) where
Var :: (Num a, Integral a) => a -> Var i
{- other constructors .... -}
to mam instancję Num
:
instance Num (Var i) where
(Var a) + (Var b) = Var (a + b)
Oczywiście to nie zadziała. Typ a
jest ukryty przez konstruktora, ponieważ typ Var
to forall (i :: Nat) a. Num a => a -> Var i
. Należy również zauważyć, że konstruktor Var
nie jest przeznaczony do bezpośredniego użycia; Var
s są tworzone przez inteligentnego konstruktora, który gwarantuje Var i0 ~ Var i1 => a0 ~ a1
. Typ Var nie może być Var i a
; chodzi o to, aby ukryć typ przed użytkownikiem.
Jak mogę powiedzieć systemowi, co "sprawdziłem", że jest prawdziwe, a mianowicie, że Var i0 ~ Var i1 => a0 ~ a1
. Obecnie używam unsafeCoerce
:
(Var (a :: n)) + (Var b) = Var (a + (unsafeCoerce b :: n))
Zdaję sobie sprawę, że unsafeCoerce
jest w twierdzeniu, że dwa typy są równe, ale chciałbym spróbować zrobić to twierdzenie na poziomie typu, dzięki czemu eksportu ISN konstruktora” t niebezpieczne. Przez niebezpieczne Znaczy następuje to możliwe:
>instance Show (Var i) where {show (Var a) = "Var " ++ show a}
>import Data.Word
>Var (1000 :: Word16) + Var (255 :: Word8)
Var 1255
>Var (255 :: Word8) + Var (1000 :: Word16)
Var 231
Próbowałem typów rodzin, ale na odwrót (np. 'Var :: a -> Var (Of a)'). Obecny program używa tylko około 8 różnych typów dla konstruktora 'Var', więc zapewnienie jawnego mapowania nie powinno być trudne. Ale chciałbym sprawdzić, czy jest inaczej. – user2407038