Type Level Literals - Невозможно использовать по-разному типизированные параметры в двоичной функции

Воспроизведение Типовых литералов как способ различения непустых значений контейнера (как в случае с фантомных типов) с использованием типа типа Maybe.

This works well. (required GHC >= 7.6.1)

Но пытаясь определить двоичную функцию (eq)

eq :: (Eq a) => TMaybe (sym :: Symbol) a -> TMaybe (sym :: Symbol) a -> Bool

который допускает разные группы значений, сигнализирует об ошибке компиляции, когда он используется:

Не удалось совместить тип «Просто» с «Nothing»

{-# LANGUAGE DataKinds, KindSignatures, GADTs, FlexibleInstances #-} 

import GHC.TypeLits

data TMaybe :: Symbol -> * -> * where
  TNothing  :: TMaybe "Nothing" a
  TJust :: a -> TMaybe "Just" a

nonEmpty :: Maybe a -> TMaybe "Just" a
nonEmpty (Just x) = TJust x
nonEmpty Nothing = error "invalid nonEmpty data"

-- this fromJust rejects TNothing at compile time  
fromJust :: (sym ~ "Just") => TMaybe (sym :: Symbol) a -> a
fromJust (TJust x) = x

tmbToMaybe :: TMaybe (sym :: Symbol) a -> Maybe a
tmbToMaybe TNothing = Nothing
tmbToMaybe (TJust x) = Just x

mbToTNothing Nothing = TNothing

mbToTJust (Just x) = TJust x

instance Eq a => Eq (TMaybe (sym :: Symbol) a) where
     TNothing == TNothing = True
     TJust x == TJust y = x == y
     _ == _ = False    -- useless, equal types required

instance Ord a => Ord (TMaybe (sym :: Symbol) a) where
     compare TNothing TNothing = EQ
     compare (TJust x) (TJust y) = Prelude.compare x y
     compare TNothing _ = LT   -- useless, equal types required
     compare _ TNothing = GT   -- useless, equal types required

instance  Functor (TMaybe (sym :: Symbol))  where
    fmap _ TNothing       = TNothing
    fmap f (TJust a)      = TJust (f a)

instance  Monad (TMaybe "Just") where
    (TJust x) >>= k      = k x

    (TJust _) >>  k      = k

    return            = TJust
    fail _              = error "can't fail to TNothing"

--------------------------

-- defining eq to admit parameter types with different symbol

eq :: (Eq a) => TMaybe (sym :: Symbol) a -> TMaybe (sym :: Symbol) a -> Bool
eq TNothing TNothing = True
eq (TJust x) (TJust y) = x == y
eq _ _ = False

---------------------------

-- Test

main = do
        print $ fromJust $ TJust (5::Int)   -- type-checks
        -- print $ fromJust TNothing   -- as expected, does not type-check

        -- print $ TNothing == TJust (5::Int)  -- as expected, does not type-check, types required equal at function def.
        print $ TNothing `eq` TJust (5::Int)   -- does not type-check either
5

1 ответы

Ну, твой тип

eq :: (Eq a) => TMaybe (sym :: Symbol) a -> TMaybe (sym :: Symbol) a -> Bool

требует, чтобы оба аргумента имели один и тот же тип, поэтому, конечно, компилятор отклонит попытку сравнить TMaybe «Nothing» a и TMaybe «Just» a .

Если вы измените тип на

eq :: (Eq a) => TMaybe (sym :: Symbol) a -> TMaybe (sym1 :: Symbol) a -> Bool

он компилирует и

TNothing `eq` TJust (5::Int)

оценивает False . (Тем не менее, вам нужно явно определить тип TNothing s во многих местах.)

9
добавлено
спасибо, с добрым материалом, загромождающим тип, я не обращал внимания на переменную '' sym ''
добавлено автор Gabriel Riba, источник
Haskell
Haskell
910 участник(ов)

https://combot.org/chat/-1001043143583 Ссылки на полезные ресурсы: https://ruhaskell.org/links.html ;