One interesting thing that Rust has is structure union syntax sugar with pattern matching that resembles a functional language like Ocaml or Haskell. (or at least resembles a functional language infused with C++ syntax like some kind of programming language Lovecraftian hybrid.)
enum OptionalInt {
Value(int),
Missing,
}
let x = Value(5);
match x {
Value(n) => println!("x is {:d}", n), // {:d}" pringles guy is horrified
Missing => println!("x is missing!"),
}
In Nim more typing is required:
const missing_is_zero = 0
type
OptionalInt_discriminator_enum = enum
int,
Missing
OptionalInt = object
case OptionalInt_discriminator: OptionalInt_discriminator_enum
of int: int_Val: int
of Missing: Missing_field: int
var an_OptionalInt = OptionalInt(OptionalInt_discriminator: int, int_Val: 2)
var another_OptionalInt = OptionalInt(OptionalInt_discriminator: Missing, Missing_field: missing_is_zero)
proc print_it(it: OptionalInt) =
case it.OptionalInt_discriminator:
of int: echo "x is ", it.int_Val
of Missing: echo "x is missing!"
Ocaml:
type OptionalInt =
| Value of int
| Missing
let an_OptionalInt = Value 2
let another_OptionalInt = Missing
match it with
| Value(n) -> print_endline(to_string(n))
| Missing -> print_endline("x is missing!");;
###############################
## Library code
type
TMaybeKind = enum
mkJust,
mkNothing
TMaybe[T] = object
case kind: TMaybeKind
of mkJust: val: T
of mkNothing: noVal: int
proc eval[T](a: TMaybe[T], justProc: proc (x: T), nothingProc: proc ()) =
case a.kind
of mkJust: justProc(a.val)
of mkNothing: nothingProc()
proc just[T](v: T): TMaybe[T] = TMaybe[T](kind: mkJust, val: v)
proc nothing(): TMaybe[int] = TMaybe[int](kind: mkNothing, noVal: 0)
###############################
## Application code
proc prVal[T](val: T) = echo "val is " & $val
proc prNo() = echo "no val"
let
a = just(7)
b = nothing()
c = just("hello world")
eval(a, prVal[int], prNo)
eval(b, prVal[int], prNo)
eval(c, prVal[string], prNo)
Not as terse or pretty as Haskell, but maybe gets the job done.
Actually, the structure union of nil and pointer count as an optional type. You could implement nil in a hypothetical language like this:
(stolen from Qi lisp)
is_nil?(it: int) =
if cast[int](it) == 0:
return true
else:
return false
is_not_nil?(it: int) =
if cast[int](it) != 0:
return true
else:
return false
datatype nil:
is_nil?( X: int )
-------------------------------
X : nil
datatype not_nil_ptr:
is_not_nil?(X: int)
----------------------------
X : not_nil_ptr
datatype nilable_ptr:
X : nil
--------------------
X : nilable_ptr;
X : not_nil_ptr
----------------------
X : nilable_ptr
proc deref_ptr(it: not_nil_ptr) =
# safe because not_nil_ptr type is known
# to have passed is_not_nil? test
proc print_it(it: nilable_ptr) =
match it:
nil --> echo "nil"
not_nil_ptr --> echo "pointer adress: ", it
# compiler generates
proc print_it(it: nilable_ptr) =
if is_nil?(it):
echo "nil"
return
else:
echo "pointer adress: ", it
return
Did it ever occur to you that we know how Rust/Haskell/ML does it and that the way Nim does it also has its advantages?
type
NodeKind = enum opValue, opAdd, opSub, opMul, opCall
Node = ref object
case k: NodeKind
of opValue: value: int
of opAdd, opSub, opMul, opCall: kids: seq[Node]
proc safeLen(n: Node): int = if n.k == opValue: 0 else: n.kids.len
proc sameTree(a, b: Node): bool =
if a.k == b.k:
if a.k == opValue: result = a.value == b.value
elif a.kids.len == b.kids.len:
result = true
for i in 0..<a.kids.len:
if not sameTree(a.kids[i], b.kids[i]): return false
proc containsSubtree(n, subtree: Node): bool =
if sameTree(n, subtree): return true
for i in 0 .. <n.safeLen:
if n.kids[i].containsSubtree(subtree): return true
Try this:
type Option*[T] = object
case bound*: bool
of true:
value*: T
of false: nil
# Constructors
template some*[T](e: T): Option[T] =
Option[type(e)](bound: true, value: e)
template none*(T: typedesc): auto =
Option[T](bound: false)
###########################################################
#
# Note: Unfortunately, we can't write a template `none[T]`,
# since generic parameters cannot be accessed within a
# template. Hence we use `none(T)` above, which also allows
# `T.none` to work. The following hack can be used to make
# `none[T]` work, still:
#
# type NoType = distinct bool
# const none* = NoType(false)
# template `[]`*(x: NoType, T: typedesc): auto =
# Option[T](bound: false)
#
###########################################################
# Operator access and automatic conversion to bool
proc `^`*[T](e: Option[T]): T {.inline.} =
e.value
converter `?`*[T](e: Option[T]): bool {.inline.} =
e.bound
# Deconstruction in 'if' statements
template `?=`*[T](v: expr, e: Option[T]): bool =
var v {.inject.}: type(e.value)
if e.bound:
v = e.value
e.bound
# Iteration over the single element, as in Scala
iterator items*[T](e: Option[T]): T =
if e.bound:
yield e.value
when isMainModule:
var x = some(314)
var y = none(int)
echo (?x, " ", ?y, " ", ^x)
if z ?= x:
echo z
if z ?= y:
echo z
for z in x:
echo z
import sequtils
echo toSeq(x.items)
echo toSeq(y.items)