Как определить Show для разнородных списков в Haskell - PullRequest
2 голосов
/ 28 января 2020

Я изучаю то, что Haskell может предложить на своем пути к зависимым типам, читая Thinking with types.

Вот гетерогенный список, определенный как GADT.

data HList (ts :: [Type]) where
    HNil :: HList '[]
    (:#) :: t -> HList ts -> HList (t ': ts)

infixr 5 :#

instance Eq (HList '[]) where
    HNil == HNil = True

instance (Eq t, Eq (HList ts)) => Eq (HList (t ': ts)) where
    (a :# as) == (b :# bs) = a == b && as == bs


instance Ord (HList '[]) where
    compare HNil HNil = EQ

instance (Ord t, Ord (HList ts)) => Ord (HList (t ': ts)) where
    compare (a :# as) (b :# bs) = case compare a b of
                                    EQ -> compare as bs
                                    x -> x

instance Show (HList '[]) where
    show HNil = "[]"

instance (Show t, Show (HList ts)) => Show (HList (t ': ts)) where
    show xs = "[" ++ (show' xs "") ++ "]"
        where
            show' (y :# ys) prefix = prefix ++ (show y) ++ rest
                where rest = case  of
                               HNil -> ""
                               _ -> show' ys ", "

Вот ошибка, которую я получаю.

    • Occurs check: cannot construct the infinite type: ts2 ~ t1 : ts2
      Expected type: HList ts2 -> [Char] -> [Char]
        Actual type: HList ts1 -> p -> p1
    • In the expression: show' ys ", "
      In a case alternative: _ -> show' ys ", "
      In the expression:
        case ys of
          HNil -> ""
          _ -> show' ys ", "
    • Relevant bindings include
        ys :: HList ts2 (bound at src/Lib.hs:43:25)
        y :: t1 (bound at src/Lib.hs:43:20)
   |
46 |                                _ -> show' ys ", "

Я читаю исходный код HList, и то, что они делают, это то, что я считаю уродливым взломом, когда они сопоставляют шаблон с строкой, возвращаемой из show ys, и если она равна [] затем остановите рекурсию в принципе, что я считаю хаком.

1 Ответ

4 голосов
/ 28 января 2020

Будет проще, если вы определите свой собственный класс и будете использовать его. Это потому, что мы не можем передать аргумент prefix стандартному методу show, но это больше не проблема в пользовательском классе.

class HShow ts where
    hshow :: HList ts -> String -> String

instance HShow '[] where
    hshow HNil _ = ""

instance (Show t, HShow ts) => HShow (t ': ts) where
    hshow (y :# ys) p = p ++ show y ++ hshow ys ", "

После этого мы можем добавить скобки и получите стандартный Show экземпляр класса:

instance HShow ts => Show (HList ts) where
    show xs = "[" ++ hshow xs "" ++ "]"

test :: HList '[ Int, Char, Bool ]
test = 42 :# 'a' :# True :# HNil

-- > test
-- [42, 'a', True]

Альтернатива, основанная на семействах типов. Не особо проще.

type family All (c :: a -> Constraint) (xs :: [a]) :: Constraint where
  All c '[] = ()
  All c (x ': xs) = (c x, All c xs)

hshow :: All Show ts => HList ts -> String
hshow HNil        = ""
hshow (x :# HNil) = show x
hshow (x :# xs  ) = show x ++ ", " ++ hshow xs

instance All Show ts => Show (HList ts) where
  show xs = "[" ++ hshow xs ++ "]"
...