在F#中定义非零整数类型

时间:2017-08-11 02:50:39

标签: types f# integer subdomain

如何在F#中定义非零整数,在分配零值时会出现编译时错误?

我的问题是在16:00分钟观看 Scott Wlaschin 视频https://www.youtube.com/watch?v=E8I19uA-wGY&t=960s

我在SO中找到了这个问题的另一个答案,但都指的是动态检查(在创建时抛出异常),但这种方法并不是什么大问题,可以在任何OO而不是OO语言中完成。我所期待的是:type NonZeroInteger = int except 0或类似的东西。

2 个答案:

答案 0 :(得分:5)

在F#中,没有真正为你想做的事编译时间合同。处理此问题的F#方法是使用具有私有构造函数的类型NonZeroInteger和返回Option<NonZeroInteger>的函数。这将确保您永远不会有Some(0)

这样做基本上迫使使用您的代码的开发人员在给定错误的整数值时,在构造一个NonZeroInteger之后可能没有open Option module A = type NonZeroInteger = private | NonZeroInteger of int static member Create (v : int) : Option<NonZeroInteger> = if v = 0 then None else Some(NonZeroInteger(v)) member this.Get : int = this |> fun (NonZeroInteger v) -> v member this.Print = this |> fun (NonZeroInteger v) -> printfn "%i" v printfn "%A" (A.NonZeroInteger(0)) // error FS1093: The union cases or fields of the type 'NonZeroInteger' are not accessible from this code location let wrong = A.NonZeroInteger.Create(0) // None let right = A.NonZeroInteger.Create(-1) // Some wrong |> Option.iter (fun x -> x.Print) // Doesn't print anything right |> Option.iter (fun x -> x.Print) // Prints -1

在您的代码中,您可以随时安全地假设NonZeroIntegers实际上是非零。

private

NonZeroInteger构造函数可以阻止模块外的任何人构建<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:fillViewport="true"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height= "wrap_content" android:gravity="center_horizontal" android:background="@drawable/bg_image_login" android:orientation="vertical" android:windowSoftInputMode="stateVisible|adjustResize" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.fania.suara_project.LoginActivity"> <ImageView android:id="@+id/haha" android:background="drawable/bg_image_login" android:scaleType="fitXY" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> ,而无需通过Create函数。

这使得代码非常冗长和缓慢,但却很安全。所以这里肯定是一种权衡。

答案 1 :(得分:0)

您无法直接执行此操作,例如Scala的require

这是一种方式,有点DDD-ish:

type NonZeroInteger = private NonZeroInteger of int 
let createNZI i = 
  if i = 0 then
    Error "NonZeroInteger can't be Zero"
  else 
    Ok (NonZeroInteger i)

构造函数是私有的,因此您必须通过createNZI函数(如果更自然,可以切换名称)。比处理Result类型。如果更简单,可以使用Some / None。最后,如果需要,使用模式匹配或活动模式打开NonzeroInteger。