コンパイラが両方の合計型が引数として{ name :: String }
を持っていると推測できないのかどうかはわかりません。私はコンパイラがこれを今できるとは思っていませんし、それが可能であるかどうかはわかりません。
これは、使用するタイプをイントロスペクトする方法があり、Person
タイプで機能するようにpersonToString
関数を定義することができます。これは言語のより高度な領域を掘り下げていることを覚えておいてください。これは私にとっても新しい分野です。これはおそらくあなたの疑問を超えて進んでいますが、他の人にとっては役に立つかもしれませんし、可能なことを知っておくとよいでしょう。
まず、「名前を持つ型」のtypecassを定義しましょう。
class DoesHaveName a where
getName :: a -> String
ここで、Person
タイプの構造を調べる必要があります。そのためにはpurescript-generics-repパッケージを使用することができます。最初に、コンパイラにデータ型を調べ、汎用型の表現を作成するように指示します。 Person
タイプの場合はGeneric
のインスタンスを作成します。
import Data.Generic.Rep (class Generic)
derive instance genericPerson :: Generic Person _
我々はData.Generic.Repにコンストラクタを見て、種類を表すために、すべてのさまざまな方法を見ることができる、と私たちはfromを使用して、その構造にPerson
を変換することができます。
import Data.Generic.Rep (class Generic, from)
personToString :: Person -> String
personToString a = getName (from a)
は、だから今、私たちは{ name :: String }
を受け入れるいずれかの引数のコンストラクタのためDoesHaveName
のインスタンスを作成する必要があります。
import Data.Generic.Rep (class Generic, to, from, Sum(..), Rec(..), NoConstructors, Constructor(..), Field(..))
import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol)
instance doesHaveNameConstructor
:: (IsSymbol t0, IsSymbol t1)
=> DoesHaveName (Constructor t0 (Rec (Field t1 String))) where
getName (Constructor (Rec (Field c))) =
case (reflectSymbol (SProxy :: SProxy t1)) of
"name" -> c
_ -> "NoName"
これはたくさんあります。私はできる限り最善を尽くして試してみよう。 t0
とt1
はシンボルです - あなたが書くリテラルコードの一部です。この場合、t0
はSum型コンストラクタ(AmyまたはGeorge)の名前です。 t1
はレコードのラベルです(この例では「名前」になります)。したがって、reflectSymbol
を使用して、シンボルを一致させることができる文字列に変換します。ラベルが "name"の場合、フィールド内の値が返されます。そうでなければ "NoName"が返されます。
最後に、Sum型構造のインスタンスをDoesHaveName
に作成する必要があります。 Sum型にはコンストラクタが含まれているため、このインスタンスは基本的には外部構造を処理しており、上で定義したインスタンスに委譲しています。
instance doesHaveNameSum
:: (DoesHaveName a, DoesHaveName b)
=> DoesHaveName (Sum a b) where
getName (Inl a) = getName a
getName (Inr b) = getName b
今、私たちは人の名前のすべての種類のログを記録することができます...
data Person
= Amy { name :: String }
| George { name :: String }
| Jim { name :: String }
-- Logs "amy"
log $ personToString (Amy { name: "amy" }
-- Logs "george"
log $ personToString (George { name: "george" }
-- Logs "jim"
log $ personToString (Jim { name: "jim" }
デモ:http://try.purescript.org/?gist=2fc95ad13963e96dd2a49b41f5703e21コンストラクタが無視できる場合
なぜそれを変数にマッチさせ、その上に 'name'を使用するのですか? 'personToString p = name p' – arrowd
確かに可能でしたが、何が起きているのかを理解するために、この人工的な例を使用していました... –