如何约束输入类型和输出类型是一样的?

时间:2017-08-13 10:06:52

标签: type-safety dependent-type idris

我正在与Manning的Idris进行类型驱动开发。给出了一个示例,教导如何将函数限制为类型族中的给定类型。我们的Vehicle类型使用PowerSourcePedal Petrol,我们需要编写一个函数refill,仅对使用汽油的车辆进行类型检查他们的能源。

以下代码有效,但不保证重新填充Car会生成Car而不是Bus。如何更改refill函数的签名,以便在给定Car时生成Car并在给定Bus时生成Bus

data PowerSource
  = Pedal
  | Petrol

data Vehicle : PowerSource -> Type 
  where
    Bicycle : Vehicle Pedal
    Car : (fuel : Nat) -> Vehicle Petrol
    Bus : (fuel : Nat) -> Vehicle Petrol

refuel : Vehicle Petrol -> Nat -> Vehicle Petrol
refuel (Car fuel) x        = Car (fuel + x)
refuel (Bus fuel) x        = Bus (fuel + x)

2 个答案:

答案 0 :(得分:5)

这可以通过引入新的VehicleType数据类型并向Vehicle添加一个参数来实现,如下所示:

data VehicleType = BicycleT | CarT | BusT

data Vehicle : PowerSource -> VehicleType -> Type 
  where
    Bicycle :                 Vehicle Pedal  BicycleT
    Car     : (fuel : Nat) -> Vehicle Petrol CarT
    Bus     : (fuel : Nat) -> Vehicle Petrol BusT

你应该以某种方式编码构造函数之间的类型差异。如果您想要更多类型安全,则需要向类型添加更多信息。然后,您可以使用它来实现refuel函数:

refuel : Vehicle Petrol t -> Nat -> Vehicle Petrol t
refuel (Car fuel) x        = Car (fuel + x)
refuel (Bus fuel) x        = Bus (fuel + x)

更换

refuel (Car fuel) x        = Car (fuel + x)

refuel (Car fuel) x        = Bus (fuel + x)

导致下一个类型错误:

Type checking ./Fuel.idr
Fuel.idr:14:8:When checking right hand side of refuel with expected type
        Vehicle Petrol CarT

Type mismatch between
        Vehicle Petrol BusT (Type of Bus fuel)
and
        Vehicle Petrol CarT (Expected type)

Specifically:
        Type mismatch between
                BusT
        and
                CarT

答案 1 :(得分:2)

另一种可能性是在外部做你想做的事。当您无法更改原始类型时,可能会选择此选项,例如如果它来自图书馆。或者,如果您不想使用太多额外的索引来混淆您的类型,您可能希望添加更多属性。

让我们重用@Shersh引入的VehicleType类型:

data VehicleType = BicycleT | CarT | BusT

现在,让我们定义一个函数,告诉我们使用哪个构造函数来构建车辆。它允许我们陈述我们的财产退出consice:

total
vehicleType : Vehicle t -> VehicleType
vehicleType Bicycle = BicycleT
vehicleType (Car _) = CarT
vehicleType (Bus _) = BusT

现在我们可以说refuel保留了车辆的类型:

total
refuelPreservesVehicleType : vehicleType (refuel v x) = vehicleType v
refuelPreservesVehicleType {v = (Car _)} = Refl
refuelPreservesVehicleType {v = (Bus _)} = Refl