2011-06-08 2 views
14

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

{-# 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' 

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

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

+1

То, что вы пытаетесь сделать здесь, имеет смысл, но оно не очень хорошо поддерживается Haskell и обычно является симптомом неидиоматического дизайна. Если вам интересно, как это сделать, @hammar дает вам отправную точку. Если вы столкнетесь с этой ситуацией в реальном коде, вам, вероятно, будет лучше переосмыслить ваш подход. –

ответ

9

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

Добавление некоторых оберток делает его работу:

{-# 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")]] 

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

+2

Обратите внимание, что тип, который вы определили, отличается от типа вопроса: '∀ x. C x => [x] 'отличается от' [∀ x. C x => x] '. –

+0

@camccann: Вы уверены, что читаете мой код правильно? 'SomeWrapper'' forall x. C x => x', и я использую '[SomeWrapper]'. – hammar

+0

@hammar: Да, и вопрос имеет это в другую сторону. Сравните определения 'SomeGroup'. –

3

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

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

{-# 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-элементов в списке, но синтаксис никогда не будет таким же приятным, как встроенный синтаксис Haskell!

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

 Смежные вопросы

  • Нет связанных вопросов^_^