PureScript - 实例声明中出现的所有类型必须采用 T a_1 .. a_n 形式

PureScript - All types appearing in instance declarations must be of the form T a_1 .. a_n

考虑以下 PureScript 代码块,它定义了一个 showCustomer 类型 class。

module Main where

import Prelude
import Effect 
import Effect.Console 

class Show a where
  show :: a -> String

type Customer = {
  name :: String
}

customer :: Customer
customer = {
  name : "Daniel Stern"
}

instance showCustomer :: Show Customer where
  show a = "A customer"

但是此代码会产生以下错误,

Type class instance head is invalid due to use of type
  
    ( name :: String
    )
  
All types appearing in instance declarations must be of the form T a_1 .. a_n, where each type a_i is of the same form, unless the type is fully determined by other type class arguments via functional dependencies.

问题:

  1. 如果传递给 show 的值是 Customer 类型,如何创建记录“客户”的 showCustomer 方法?

  2. 错误是什么意思?

type 只是一个类型同义词(就像 c 中的 typedef)。所以在编译过程中,

 customer :: Customer 

会变成

customer ::  {name :: String}

所以你不能直接为type创建实例。

如果你真的想创建实例,用newtype包装它然后为它创建一个实例。

newtype Customer = MkCustomer {
  name :: String
}

customer :: Customer
customer = MkCustomer { name : "Daniel Stern"}

instance showCustomer :: Show Customer where
  show (MkCustomer customer) = customer.name

虽然我不知道这个错误是什么意思

错误的字面意思是它所说的:实例头必须看起来像 T x y z,而你的看起来像 { name :: String }

根本原因是 PureScript 不支持记录实例。对不起,运气不好。没办法。


如果您希望您的类型具有实例,它们必须是 newtypedata,例如:

newtype Customer = Customer { name :: String }

但这有其自身的缺点:Customer 不再是记录,这意味着您不能再通过点访问其字段。你必须先打开类型才能得到包裹在里面的记录,然后你才能访问它的字段:

c = Customer { name: "John" }

doesntWork = c.name

works = let (Customer x) = c in x.name

-- also works:
getName :: Customer -> String
getName (Customer x) = x.name

还有一种(稍微)更短的方法可以使用 Newtype class:

import Data.Newtype (class Newtype, unwrap)

newtype Customer = Customer { name :: String }
derive instance Newtype Customer _

c :: Customer
c = Customer { name: "John" }

john :: String
john = (unwrap c).name