F# 我如何重构,使得分超过特定数量的分数是不可表示的?
如何进行重构,使得分超过特定数量的分数是不可表示的? 例如,我如何使用下面的代码并使编译器拒绝任何超过总分11分的快照F# 我如何重构,使得分超过特定数量的分数是不可表示的?,f#,refinement-type,F#,Refinement Type,如何进行重构,使得分超过特定数量的分数是不可表示的? 例如,我如何使用下面的代码并使编译器拒绝任何超过总分11分的快照 let results = (player1, player2) |> makeFieldBasket TwoPointer |> makeFoulShots ThreeFoulShots |> makeFieldBasket
let results = (player1, player2) |> makeFieldBasket TwoPointer
|> makeFoulShots ThreeFoulShots
|> makeFieldBasket TwoPointer
|> makeFoulShots TwoFoulShots
|> makeFieldBasket TwoPointer
上述代码的输出如下所示:
val results : FoulShooter * FieldShooter =
(FoulShooter {Score = 11;}, FieldShooter {Score = 0;})
现在我想构造我的代码,这样就不能编译额外的快照了
例如,我希望编译器拒绝超过11分的额外犯规:
let results = (player1, player2) |> makeFieldBasket TwoPointer
|> makeFoulShots ThreeFoulShots
|> makeFieldBasket TwoPointer
|> makeFoulShots TwoFoulShots
|> makeFieldBasket TwoPointer
|> makeFoulShots FoulShot
目前,上述代码是合法的
整个代码如下:
(*Types*)
type Player = { Score:int }
type FieldShot = TwoPointer| ThreePointer
type FoulShots = FoulShot | TwoFoulShots | ThreeFoulShots
type FoulShooter = FoulShooter of Player
type FieldShooter = FieldShooter of Player
(*Functions*)
let shoot lastShot player =
(player.Score + lastShot)
let fieldShot (fieldShooter, shot) =
let player = match fieldShooter with
| FieldShooter player -> player
match player.Score with
| score when score >= 11 -> score
| _ -> match (fieldShooter, shot) with
| FieldShooter player, shot -> match shot with
| TwoPointer -> player |> shoot 2
| ThreePointer -> player |> shoot 3
let foulShot (foulShooter, shot) =
let player = match foulShooter with
| FoulShooter player -> player
match player.Score with
| score when score >= 11 -> score
| _ -> match (foulShooter, shot) with
| FoulShooter player, shot -> match shot with
| FoulShot -> player |> shoot 1
| TwoFoulShots -> player |> shoot 2
| ThreeFoulShots -> player |> shoot 3
let makeFoulShots foulShots (shooter, defender) =
FieldShooter { Score= foulShot (shooter, foulShots) }, defender
let makeFieldBasket fieldBasket (shooter, defender) =
FoulShooter { Score= fieldShot (shooter, fieldBasket) }, defender
let turnover (shooter, defender) = (defender, shooter)
(*Client*)
let player1, player2 = FieldShooter { Score=0 } ,
FieldShooter { Score=0 }
let results = (player1, player2) |> makeFieldBasket TwoPointer
|> makeFoulShots ThreeFoulShots
|> makeFieldBasket TwoPointer
|> makeFoulShots TwoFoulShots
|> makeFieldBasket TwoPointer
|> makeFoulShots FoulShot
就我所知,没有办法在编译时实现这一点。自定义数字类型可以帮助您将操作保持在有效范围内,并表示(或失败时)溢出,但在编译时没有帮助。(Ab)使用计量单位也会有同样的问题。我认为您必须在代码上运行一个定理证明程序,以确定重复添加点将导致超出有效范围。据我所知,在编译时无法实现这一点。自定义数字类型可以帮助您将操作保持在有效范围内,并表示(或失败时)溢出,但在编译时没有帮助。(Ab)使用计量单位也会有同样的问题。我认为您必须在代码上运行一个定理证明程序,以确定重复添加点将导致您超出有效范围。您希望的功能(并且您不是唯一的!)称为依赖类型(,和)。更准确地说,您的特定示例将被称为细化类型,因为类型
Score
对值n
的依赖性由谓词表示,在本例中,n您想要的特性(而您不是唯一的!)称为依赖类型(,和a)。更准确地说,您的特定示例将被称为细化类型,因为类型Score
对值n
的依赖性由谓词表示,在本例中n您是否厌倦了使用枚举?为什么希望编译器捕获错误而不是运行时?我很确定你可以让编译器来做这件事,但我不知道F#有什么共同点。看看答案,或者也许有一种方法是滥用度量单位,将分数设为points=1.0
@ScottNimrod一种可能性-创建自定义数字类型:这并不容易,但可以工作“我认为这是函数式编程的一个巨大卖点?”只是逐渐地,而且只适用于一部分语言。有些FP语言,如Clojure和Erlang,根本没有静态类型。即使是静态类型语言也只能在一定程度上通过其类型系统实现域规则。我发现F#的类型系统比C#的强,因为它允许我将一些规则编码为我在C#中无法做到的类型。Haskell的类型系统,依我看,比F#强,然后Idris据说更强大(但我没有经验)。IIRC,几年前我发现了一些启发。你厌倦使用枚举了吗?为什么希望编译器捕获错误而不是运行时?我很确定你可以让编译器来做这件事,但我不知道F#有什么共同点。看看答案,或者也许有一种方法是滥用度量单位,将分数设为points=1.0
@ScottNimrod一种可能性-创建自定义数字类型:这并不容易,但可以工作“我认为这是函数式编程的一个巨大卖点?”只是逐渐地,而且只适用于一部分语言。有些FP语言,如Clojure和Erlang,根本没有静态类型。即使是静态类型语言也只能在一定程度上通过其类型系统实现域规则。我发现F#的类型系统比C#的强,因为它允许我将一些规则编码为我在C#中无法做到的类型。哈斯凯尔的类型系统,依我看,比F#的“更强”,然后伊德里斯据说更强大(但我没有这方面的经验)。几年前我发现IIRC很有启发性。
type Score = InvalidScore | ValidScore of int<pts>
let (+) s1 s2 = match (s1, s2) with
| ValidScore a, ValidScore b when (a + b) <= 11<pts> -> ValidScore (a + b)
| _ -> InvalidScore
[<AutoOpen>]
module Score =
type Score = private InvalidScore | ValidScore of int<pts> with
static member Create n =
if n > 11<pts> then InvalidScore else ValidScore n
member this.GetPoints =
match this with
| InvalidScore -> None
| ValidScore x -> Some x
let (+) s1 s2 =
match (s1, s2) with
| ValidScore a, ValidScore b when (a + b) <= 11<pts> -> ValidScore (a + b)
| _ -> InvalidScore
let x = ValidScore 12<pts> // won't compile
let y = Score.Create 12<pts> // compiles, but if you call y.GetPoints you get None