Now, I'm not saying that this operator is necessarily a good thing, but I was wondering, could one implement an operator like this in nimrod? If so, how?
type Node* = ref object
child*: Node
template `?.`*(n:Node, c:expr): Node {.immediate.} =
if n.isNil: nil else: n.c
proc main =
var parent = Node(
child: Node(
child: Node(
child: nil)))
var a = parent?.child?.child
var b = parent?.child?.child?.child
var c = parent?.child?.child?.child?.child
assert a != nil
assert b == nil
assert c == nil
main()
template `@.`* (n :Node, c: expr): Node = (let m = n; if m.isNil: nil else: m.c)
type ISafeNode* = generic x
isNil(x) is bool
template `?.`*(n: ISafeNode, c: expr): expr =
if isNil(n): nil else: n.c
var stuff:seq[string] = getStuff()
if stuff?.len > 0:
nextThing()
I don't think that would work.. len returns an int which is not nullable, so it would be:
null.len > 0
Oddly enough, the above code works if one turns on Araq's new strongspaces feature, as this causes the code to be grouped like so:
var stuff:seq[string] = getStuff()
if (stuff?.len) > 0:
nextThing()
Of course, this code still doesn't compile (because you can't compare nil > 0) and you still get a syntax error if you use len() instead of len, so maybe something like a safe access operator needs some fine tuning.Right, what I was trying to actually do would require a slightly related but different type of template, basically to just do the nil check for me and short-circuit at runtime in something like a condition of an if statement. Need to study up on templates to learn the basics.
One other thing that came up which seems perhaps some related are some compiler flags and code for --nilCheck:on. Seems something like that may have been available several months ago but things have changed.
Experimenting with this I attempted to modify the ?. template to return a default value instead of nil, so instead of a nil sequence it would return an empty one over which the len proc could work, but somehow I couldn't make it work properly in all conditions so I ended settling with a second safe proc.
This safe proc has problems too, it only works for read access. Ideally the returned default value would follow the so called null object pattern and one would be able to write:
var a: string
a.safe.add("stuff which goes to /dev/null")
And know that the object representing the null object would not be modified. I experimented with var return values and distinct types but the meta was again over my level.Did Nim ever get anything like this?
Here's an example of the two null-safe features that are baked in most modern languages (PHP, Groovy, Kotlin...)
someVariable?.someMethod("argument") ?: "otherwise"
| | |
| | | elvis operator: if the left-side is not null,
| | | return its value and don't execute the right side,
| | \_ otherwise execute it and return its value
| |
| | null-safe call operator: if the left-side is null, the procedure is never
| | called and the result of the expression is null or void, depending on the
| \_ return value of someMethod (whether it's a nullable value or void)
|
\_ a variable that can be null
This immediately leave the proc when across something nil
template safe(obj: typed, field: untyped): untyped =
if obj.isNil:
return
`obj`.`field`
type
MyNullObj = ref object
x: int
proc main =
var nullobj: MyNullObj
var notnull = MyNullObj(x: 5)
echo notnull.safe(x)
echo "reach here"
echo nullobj.safe(x)
proc main2 =
var nullobj: MyNullObj
var notnull = MyNullObj(x: 5)
echo nullobj.safe(x)
echo "cannot reach here"
echo notnull.safe(x)
main2()
main()
For proc that need to return something, have to set result in the function before hand, but result always has its default value so it's also ok
Did Nim ever get anything like this?
No and Nim is not full of nullable types to begin with (unlike PHP, Kotlin, ...) In Nim mostly ref introduces these problems and there is an RFC for addressing this problem with static analysis instead of patching over it via more syntactic sugar.