我的代码中有几种域类型用于区分不同类型的字符串,因此编译器可以阻止我以错误的顺序传递参数:
type Foo = string
type Bar = string
let baz (foo : Foo) (bar : Bar) = printfn "%A %A" foo bar
let f : Foo = "foo"
let b : Bar = "bar"
baz f b // this should be OK
baz b f // this shouldn't compile
然而,由于两个原因,这目前无法令人满意地工作:
null
不是有效值的方法,因此我无法保证Foo
实例永远不会{ {1}}。有没有办法定义
类型的别名 a)引用/包装相同的类型,但彼此不兼容,并且
b)不允许null
值,即使基础类型允许它?
答案 0 :(得分:1)
@kvb答案的一个变体是使用来自C ++的老式技巧,它依赖于“标签”类型来创建显着的别名(C ++ typedef是别名因此具有与F#类别名相同的优点和缺点)
F#4也不支持struct ADT(但F#4.1会这样做),因此使用ADT会在堆上创建更多对象。我的示例使用struct类型来缓解堆压力。
根据我个人的偏好,我认为空字符串与空字符串“相同”,所以我认为不是抛出一个就可以将null视为空。
// NonNullString coalesces null values into empty strings
type NonNullString<'Tag>(s : string) =
struct
member x.AsString = if s <> null then s else ""
override x.ToString () = x.AsString
static member OfString s = NonNullString<'Tag> s
end
// Some tags that will be used when we create the type aliases
type FooTag = FooTag
type BarTag = BarTag
// The type aliases
type Foo = NonNullString<FooTag>
type Bar = NonNullString<BarTag>
// The function
let baz (foo : Foo) (bar : Bar) = printfn "%A, %A" foo.AsString.Length bar.AsString.Length
[<EntryPoint>]
let main argv =
// Some tests
baz (Foo.OfString null) (Bar.OfString "Hello")
// Won't compile
// baz (Bar.OfString null) (Bar.OfString "Hello")
// baz "" (Bar.OfString "Hello")
0
答案 1 :(得分:0)
这是@FuleSnabel答案的一个略微变体,它使用了我们称之为“幻像类型”的答案。我会像下面这样表达它们,我认为这有点像惯用语:
/// Strongly-typed strings.
module String_t =
type 'a t = private T of string
let of_string<'a> (s : string) : 'a t = T s
let to_string (T s) = s
type foo = interface end
type bar = interface end
let baz (foo : foo String_t.t) (bar : bar String_t.t) =
printfn "%s %s" (String_t.to_string foo) (String_t.to_string bar)
let f : foo String_t.t = String_t.of_string<foo> "foo"
let b : bar String_t.t = String_t.of_string<bar> "bar"
根据上述定义,让我们尝试您的测试:
> baz f b;;
foo bar
val it : unit = ()
> baz b f;;
baz b f;;
----^
/path/to/stdin(16,5): error FS0001: Type mismatch. Expecting a
'foo String_t.t'
but given a
'bar String_t.t'
The type 'foo' does not match the type 'bar'