我不确定编译器为什么不能推断这两个和类型都有{ 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
当然可以,但我正在使用这个人为的例子来理解发生了什么...... –