我怀疑的一个简单问题。我有简单的功能定义
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的角度来看是否有意义(可能没有)?
答案 0 :(得分:0)
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,即使你将一些复杂的表达式应用于Patient构造函数。您可以安全地省略plings,但在大多数情况下包含它们是一种好习惯。
您现在可以使用各种形式的案例分析从这样的患者值中提取元素。您将在手册中看到所有这些内容,但这里有一个摘要:
age p =
case p of
Patientname age -> age; // Spot the maintenance problem here!
;
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.
如果需要,您始终可以根据此数据投影记录。 请记住,如果有多种患者,您还可以为患者数据类型设置多个构造函数。
例如,也许有住院病人和门诊病人。这两者都有一些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功能,它可以进行多个构造函数匹配。在这种情况下,我们匹配InPatient和OutPatient,仅投射patientRecords字段。
这使我们能够编写一个'nhsRecords'提取器功能,即使患者构造器中的细节发生变化,我们也能轻松维护这些功能。
实际上,除非构造者来来去去,或者“patientRecords”字段本身发生了什么,否则这个函数永远不会改变。