Using Option
The Option type was introduced earlier, defining its characteristics. Since the Option type can represent both a value and the absence of a value (where the absence may be interpreted as an error in certain contexts), it can also be used for error handling.
For example, in the following case, if the parameter value of the function getOrThrow equals Some(v), it returns the value v; if the parameter equals None, it throws an exception.
func getOrThrow(a: ?Int64) {
match (a) {
case Some(v) => v
case None => throw NoneValueException()
}
}
Because Option is an extremely common type, Cangjie provides multiple destructuring methods to facilitate its usage, including: pattern matching, the getOrThrow function, the coalescing operator (??), and the question mark operator (?). Each of these methods will be explained in detail below.
-
Pattern Matching: Since the Option type is an enum type, the pattern matching for enums mentioned earlier can be used to destructure
Optionvalues. For example, in the following functiongetString, which takes a parameter of type?Int64, if the parameter is aSomevalue, it returns the string representation of the contained number; if the parameter isNone, it returns the string"none".func getString(p: ?Int64): String{ match (p) { case Some(x) => "${x}" case None => "none" } } main() { let a = Some(1) let b: ?Int64 = None let r1 = getString(a) let r2 = getString(b) println(r1) println(r2) }The execution result of the above code is:
1 none -
Coalescing Operator (
??): For an expressione1of type?T, if you want to return a valuee2of typeTwhene1equalsNone, you can use the??operator. For the expressione1 ?? e2, ife1equalsSome(v), it returnsv; otherwise, it returnse2. Example:main() { let a = Some(1) let b: ?Int64 = None let r1: Int64 = a ?? 0 let r2: Int64 = b ?? 0 println(r1) println(r2) }The execution result of the above code is:
1 0 -
Question Mark Operator (
?): The?operator must be used in conjunction with.,(),[], or{}(specifically in trailing lambda calls) to enable support for these operations onOptiontypes. Taking.as an example (similarly for(),[], and{}), for an expressioneof type?T1, wheneequalsSome(v), the value ofe?.bisOption<T2>.Some(v.b); otherwise, it isOption<T2>.None, whereT2is the type ofv.b. Example:struct R { public var a: Int64 public init(a: Int64) { this.a = a } } let r = R(100) let x = Some(r) let y = Option<R>.None let r1 = x?.a // r1 = Option<Int64>.Some(100) let r2 = y?.a // r2 = Option<Int64>.None class C { var item: Int64 = 100 } let c = C() let c1 = Option<C>.Some(c) let c2 = Option<C>.None func test1() { c1?.item = 200 // c.item = 200 c2?.item = 300 // no effect }The question mark operator (
?) supports multi-level access. For example, ina?.b.c?.d(similarly for(),[], and{}), the expressionamust be of typeOption<T1>, whereT1contains an instance memberb. The type ofbmust contain an instance memberc, andcmust be of typeOption<T2>, whereT2contains an instance memberd. The type ofa?.b.c?.disOption<T3>, whereT3is the type ofT2’s instance memberd. WhenaequalsSome(va)andva.b.cequalsSome(vc), the value ofa?.b.c?.disOption<T3>.Some(vc.d). WhenaequalsSome(va)butva.b.cequalsNone, the value isOption<T3>.None(dis not evaluated). WhenaequalsNone, the value isOption<T3>.None(b,c, anddare not evaluated).class A { public var b: B = B() } class B { public var c: Option<C> = C() public var c1: Option<C> = Option<C>.None } class C { public var d: Int64 = 100 } main(){ var a = Some(A()) let a1 = a?.b.c?.d // a1 = Option<Int64>.Some(100) let a2 = a?.b.c1?.d // a2 = Option<Int64>.None a?.b.c?.d = 200 // a.b.c.d = 200 a?.b.c1?.d = 200 // no effect } -
getOrThrowFunction: For an expressioneof type?T, you can destructure it by calling thegetOrThrowfunction. WheneequalsSome(v),getOrThrow()returnsv; otherwise, it throws an exception. Example:main() { let a = Some(1) let b: ?Int64 = None let r1 = a.getOrThrow() println(r1) try { let r2 = b.getOrThrow() } catch (e: NoneValueException) { println("b is None") } }The execution result of the above code is:
1 b is None