Список элементов типов, ограниченных классом типов - PullRequest
15 голосов
/ 09 июня 2011

Я пытаюсь закодировать список элементов, типы которых ограничены экземплярами некоторого класса типов:

{-# LANGUAGE RankNTypes, TypeSynonymInstances, LiberalTypeSynonyms #-}
module Test where

class Someable a where
  some :: a -> String

data Some = Some String

type SomeGroup = forall a. Someable a => [a]

instance Someable Some where
  some (Some v) = v

instance Someable SomeGroup where
  some (x:xs) = (some x) ++ ", " ++ (some xs)

main = do
  putStrLn $ show.some [Some "A", [Some "B", Some "C"]]

Но компиляция завершается с ошибкой:

Test.hs:14:10:
    Illegal polymorphic or qualified type: SomeGroup
    In the instance declaration for `Someable SomeGroup'

ЭтоКажется, мне даже не удалось определить экземпляр для типа синонимичного ...

Мне известно о гетерогенных коллекциях вики-статье, но я хочу знать, почему именно мой подход не работает - кажетсяДля меня естественно определить тип, ограничив коллекцию только элементами с типами, которые являются экземплярами некоторого класса типов.

Ответы [ 2 ]

9 голосов
/ 09 июня 2011

Если я правильно понимаю, должен существовать тип данных, обертывающий экзистенциал, чтобы было место для хранения словаря классов type вместе с каждым элементом.

Добавление некоторых оболочек заставляет его работать:

{-# LANGUAGE ExistentialQuantification, TypeSynonymInstances #-}

module Test where

class Someable a where
  some :: a -> String

data Some = Some String

data SomeWrapper = forall a. Someable a => SomeWrapper a

type SomeGroup = [SomeWrapper]

instance Someable Some where
  some (Some v) = v

instance Someable SomeWrapper where
  some (SomeWrapper v) = some v

instance Someable SomeGroup where
  some (x:xs) = (some x) ++ ", " ++ (some xs)

main = do
  putStrLn $ some [SomeWrapper (Some "A"), SomeWrapper [SomeWrapper (Some "B"), SomeWrapper (Some "C")]]

Конечно, это немного уродливо. К сожалению, я не знаю лучшего способа.

3 голосов
/ 09 июня 2011

Вы также можете приготовить что-нибудь, используя ГАДЦ.В некоторых случаях это может быть немного короче, и в нем четко указывается, какие словари типов доступны после сопоставления с образцом.

Вот небольшой вариант вашего примера:

{-# LANGUAGE GADTs #-} 
class Someable a where
  some :: a -> String

instance Someable Int where
  some n = show n

data SomeString = SomeString String
instance Someable SomeString where
  some (SomeString s) = s

data SomeGroup where 
    Nil :: SomeGroup
    Cons :: Someable a => a -> SomeGroup -> SomeGroup

instance Someable SomeGroup where
    some Nil = ""
    some (Cons x Nil) = some x
    some (Cons x xs) = some x ++ ", " ++ some xs

list = Cons (3::Int) (Cons (SomeString "abc") (Cons (42::Int) Nil))
main = print . some $ list

Пара небольших заметок:

  • Вы забыли базовый вариант для рекурсии:)
  • putStrLn . show совпадает с print.
  • Вы должны явно указать тип чисел как Int, поскольку целочисленные литералы обрабатываются специально, то есть 42 переводится вfromInteger 42 типа Num a => a.Немного неуклюже при построении списка напрямую, но в большинстве случаев он будет работать более плавно.
  • Конечно, вы можете определить свой собственный синтаксический сахар для элементов Cons -ing в списке, но синтаксисникогда не будет выглядеть так хорошо, как встроенный синтаксис в Haskell!

И главное, что вы теряете использование всех стандартных функций списка.Я обычно использую такое решение, когда мои потребности в обработке списка чрезвычайно ограничены;с другой стороны, сгиб написан достаточно быстро ... Ваш пробег будет отличаться, хотя, я не знаю, что вы на самом деле хотите использовать этот список для .

...