2017-09-18 10 views
4

例が考案されていますが、データコンストラクタが無視されると、ワイルドカードパターンを使用できないのはなぜですか?Purescript:パターンマッチワイルドカードデータコンストラクタ

module Main where 

import Prelude 
import Control.Monad.Eff.Console (log) 

data Person = Amy { name :: String } | George { name :: String } 

--Implementations Options Below 

main = log $ personToString $ George { name: "George" } 

エラーなし

personToString :: Person -> String 
personToString (Amy { name: n }) = n 
personToString (George { name: n }) = n 

エラー

personToString :: Person -> String 
personToString (_ { name: n }) = n 

http://try.purescript.org/?session=a1503b9a-0546-7832-39b0-6321a89ef2e3

Unable to parse module: 
    unexpected { 
    expecting ::, operator or) 
+0

なぜそれを変数にマッチさせ、その上に 'name'を使用するのですか? 'personToString p = name p' – arrowd

+0

確かに可能でしたが、何が起きているのかを理解するために、この人工的な例を使用していました... –

答えて

6

コンパイラが両方の合計型が引数として{ 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" 

これはたくさんあります。私はできる限り最善を尽くして試してみよう。 t0t1はシンボルです - あなたが書くリテラルコードの一部です。この場合、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コンストラクタが無視できる場合

0

、それはタイプがリファクタリングすることができ匂いです:

data AmyOrGeorge = Amy | George 

data Person = Person AmyOrGeorge { name :: String } 

personToString (Person _ { name: n }) = n 

私はこれを残すために言語設計者の選択に同意します実際にそれを回避しようとすると、が改善されます。コードはです。

関連する問題