I'm looking for an idiomatic way to return a default value for an object property. If the field title of MyType is not set (there's no requirement) other fields of MyType shall be checked in a predefined order. Either return the first field that is set or ultimately an empty string. For my usecase all fields are strings.
The approach below works but the if-statements are quite verbose.
type
MyType = object
title: string
foo: string
bar: string
buzz: string
proc getTitle(m: MyType): string =
if m.title != "": return m.title
if m.foo != "": return m.foo
if m.bar != "": return m.bar
return m.buzz # either set of empty
let m = MyType(bar: "Lorem ipsum", buzz: "dolor faubicus")
assert m.getTitle == "Lorem ipsum"
Javascript's short-circuit syntax provides a very compact way to express this problem. In a chain of ||-operators the first truthy value is returned (e.g. everything that's not "", [], false, undefined, null etc.) or ultimately the last value in the chain:
title = ""
foo = ""
bar = "Lorem ipsum"
buzz = "dolor faubicus"
x = title || foo || bar || buzz
x == "Lorem ipsum" // => true
However, short-circuit-evaluation is not supported in Nim.
I would rewrite this an expression if like this:
proc getTitle(m: MyType): string =
if m.title != "": m.title elif
m.foo != "": m.foo elif
m.bar != "": m.bar else:
m.buzz # either set of empty
this is still more verbose than the JS variant (though if I'm not wrong it's mostly a a byproduct of it's quirky auto conversion rules and dynamic typing). You could of course define your own operator, though that would hurt clarity, especially since fortunately not everything is nullable in Nim.
though if I'm not wrong it's mostly a a byproduct of it's quirky auto conversion rules and dynamic typing
Dynamic typing makes short-circuit-evaluation a lot more flexible. In the case above all fields have the same data type, such that an expression like result: string = m.title || m.foo || m.bar || m.buzz would not conflict with Nim's static type system.
You could of course define your own operator, though that would hurt clarity.
And would be overkill for my little usecase. Maybe there is / was already a built-in way in Nim, hence my question.
If it happens a lot, you could get the first non-empty string field of any object or tuple with a generic like so:
proc getTitle[T:tuple|object](m:T): string =
for f in m.fields:
when f is string:
if f != "": return f
if are expressions and you could rewrite getTitle like
proc getTitle(m: MyType): string =
result = if m.title != "": m.title
elif m.foo != "": m.foo
elif m.bar != "": m.bar
else: m.buzz
@spip your approach looks very similar to @doofenstein's with the difference that you're assigning the return value of the if expression to the result variable.
Do you consider the assignment to the result variable to be more idiomatic, even if the function is very simple?
@xigoi the Elvis package looks interesting. It's always great to stuble upon new packages in discussions like this.
If someone encounters the my example from above frequently it might make sense to use a package like Elvis.
proc getTitle(m: MyType): string =
if m.title != "": m.title elif m.foo != "": m.foo elif m.bar != "": m.bar else: m.buzz
seems pretty compact for the 1.5 times this will ever occur in your code in your lifetime. If you actually find yourself doing this a lot, then write a template or import Elvis.
template `?:`(a, b): auto = # left associative short-circuiting generic binary operator
if a != default(typeof(a)): a else: b
proc getTitle(m: MyType): string =
m.title ?: m.foo ?: m.bar ?: m.bar ?: m.buzz
However, short-circuit-evaluation is not supported in Nim.
Whatever you mean by this, it isn't true. It's a good idea to avoid categorical assertions about programming languages that you aren't familiar with.