15

型がいくつかの型クラスのインスタンスに制限されているアイテムのリストをエンコードしようとしています:

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

同義型のインスタンスを定義することさえできなかったようです...

異種コレクションの wiki 記事は知っていますが、私のアプローチがうまくいかない理由を正確に知りたいです。ある型クラスのインスタンスである型を持つアイテムのみを含むようにコレクションを制限することによって、型を定義するのが自然に思えます。

4

2 に答える 2

9

私が物事を正しく理解していれば、各要素とともに型クラス辞書を格納する場所を確保するために、存在をラップするデータ型が必要です。

いくつかのラッパーを追加すると、それが機能します:

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

もちろん、これは少し醜いです。残念ながら、私はこれ以上良い方法を知りません。

于 2011-06-08T20:57:51.033 に答える
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整数リテラルは特別に扱われるため、つまりtype42に変換されるため、数値の型を明示的に指定する必要があります。リストを直接構築するときは少し扱いに​​くいですが、ほとんどの場合はよりスムーズに動作します。fromInteger 42Num a => a
  • もちろん、要素をリストに追加するための独自の構文シュガーを定義することもできますConsが、その構文は Haskell の組み込み構文ほど見栄えがよくありません!

そして重要な点は、すべての標準リスト関数を使用できなくなることです。私は通常、リスト処理のニーズが非常に限られている場合に、この種のソリューションを使用します。一方、折り畳みは十分に速く書かれます...あなたのマイレージは異なりますが、このリストを実際に何に使用したいのかわかりませ

于 2011-06-09T15:36:08.360 に答える