Functional programming CAL中的用户定义记录类型?

Functional programming CAL中的用户定义记录类型?,functional-programming,Functional Programming,我怀疑这是一个简单的问题。我有一个简单的函数定义 makePatientFixture :: [ { name :: String, age :: Int} ]; makePatientFixture = [ { name = "Dave", age = 41}, { name = "Denise", age = 45}, { name = "Cameron", age = 5} ]; 我实际上想定义一个新类型,名为 Patient = { name :: String, age :: Int

我怀疑这是一个简单的问题。我有一个简单的函数定义

makePatientFixture :: [ { name :: String, age :: Int} ];
makePatientFixture = [ { name = "Dave", age = 41}, { name = "Denise", age = 45}, { name = "Cameron", age = 5} ];
我实际上想定义一个新类型,名为

Patient = { name :: String, age :: Int } 
这意味着我不必一直重复记录结构({name::String,age::Int}),相反,我的代码如下所示:

makePatientFixture :: [ Patient ];
makePatientFixture = [ { name = "Dave", age = 41}, { name = "Denise", age = 45}, { name = "Cameron", age = 5} ];

这可能吗?从CAL的角度来看,它有意义吗(可能没有)?

CAL不支持别名(Haskell使用'type'关键字实现了这一点),因此您不能只执行以下操作:

type Patient = {name::String, age::Int}
但是,您可以创建包含记录的新数据类型:

data Patient=
    public Patient
        details :: {name::String, age::Int}
    ;
。。。然而,这可能不是您需要的。记录对于移动结构化数据位和使用结构化多态性(记录子集的自动投影)非常方便。不过,您不需要像这样存储数据。 相反,我建议:

data Patient=
    public Patient
        name :: !String
        age  :: !Int
    ;
类型上的“plings”表示“不必在这里存储懒散的thunk”,例如,我们确实需要一个字符串和int,即使您将一些复杂的表达式应用于患者构造函数。您可以安全地省略这些扣环,但在大多数情况下,最好将它们包括在内

现在,您可以使用各种形式的案例分析从此类患者价值中提取元素。您将在手册中看到所有这些,但这里有一个摘要:

公开案例分析,位置匹配: 公开案例分析,符号匹配: 惰性提取器 单箱提取器 如果需要,您始终可以从此数据投影记录。 请记住,如果存在多种类型的患者,那么患者数据类型也可以有多个构造函数

例如,可能有住院病人和门诊病人。两者都有一些nhs患者记录,但都有与治疗相关的特定领域。 我们可以用以下几条线来表示:

data Patient=
    public InPatient
        patientRecords :: !PatientRecord
        careCentreID   :: !Int
        department :: !String
        consultant :: !String
    | publicOutPatient
        patientRecords :: !PatientRecord
        appointments :: ![Appointment]
    ;


nhsRecords p =
    case p of
    (InPatient|OutPatient) {patientRecords} -> patientRecords;
    ;
这也让我们可以看到一个非常强大的CAL功能,它可以进行多个构造函数匹配。在本例中,我们匹配住院患者和门诊患者,仅投影patientRecords字段

这使我们能够编写一个“nhsRecords”提取程序函数,即使患者构造器中的细节发生变化,我们也可以相当轻松地维护该函数。
事实上,除非构造函数来来去去,或者“patientRecords”字段本身发生了什么事情,否则此函数永远不需要更改

nameAndAge p =
    case p of
    Patient{name,age} -> (name,age);  // Now it doesn't matter if the Patient constructor grows new arguments
    ;
name p =
    let
        Patient{name} = p;  // name is only extracted (and p partially evaluated) if name is required
    in
        name;
name p =
    p.Patient.name;  // Syntactic sugar for example 1.  Useful when you are _only_ extracting a single field. 
data Patient=
    public InPatient
        patientRecords :: !PatientRecord
        careCentreID   :: !Int
        department :: !String
        consultant :: !String
    | publicOutPatient
        patientRecords :: !PatientRecord
        appointments :: ![Appointment]
    ;


nhsRecords p =
    case p of
    (InPatient|OutPatient) {patientRecords} -> patientRecords;
    ;