我不確定編譯器爲什麼不能推斷這兩個和類型都有{ name :: String }
作爲參數。我不認爲編譯器現在可以這樣做,我不確定這甚至是可能的。
話雖如此,有方法可以反思您使用的類型,並且您可以定義personToString
函數,以便它可以在您的Person
類型上工作。請記住,這正在研究更先進的語言領域,這也是我的一個新領域。這可能超出了你的問題,但它可能對其他人有幫助,而且知道什麼是可能的是很好的。
首先,讓我們定義一個類型類爲「有名稱的類型」。
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
將符號轉換爲我們可以匹配的字符串。如果標籤是「名稱」,那麼我們將返回字段中的值,否則我們將返回「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
當然可以,但我正在使用這個人爲的例子來理解發生了什麼...... –