type
MyType = object
str: string
method add (obj: var MyType, str: string) : MyType {. discardable .}=
obj.str = obj.str & str
return obj
proc addSimple (str: string) : string =
return MyType(str: str & " ok ").str # just an example. may also be return str & " ok "
proc `$` (obj: MyType) : string =
return obj.str
# Example with "MyType":
var mytype = MyType(str: "wow")
echo mytype.add("ok") #.add(" not ok") (more calls fail?)
# Example with string:
echo " ".addSimple.addSimple.addSimple # strings method chaining works here
echo " ".addSimple.addSimple.addSimple
This is equivalent to
echo addSimple(addSimple(addSimple(" ")))
Why would the type matter?
echo mytype.add("ok") #.add(" not ok") (more calls fail?)
This case fails because your 'add' takes a 'var' but doesn't return a 'var'. That said, please avoid C++ like method chaining via returning the this. It's bad code because it blurs the distinction between commands and queries and creates unclear ownership relations.
Why would the type matter?
@jibal My bad, I thought it would somehow matter (at least it wasn't compiling). I tried wrapping it the way you suggest, but as Araq points out I was missing 'var' as return param. :s
This case fails because your 'add' takes a 'var' but doesn't return a 'var'.
@Araq aa, I see, thank you for answering my question! :]
Please avoid C++ like method chaining via returning the this. It's bad code because it blurs the distinction between commands and queries and creates unclear ownership relations.
@Araq Hmmm, I tried to find references online to why it's bad, but could not find it. As I remember from Ruby, Php, JS such chaining is not considered "as bad"? Is it suggested somewhere in a context of CQS/CQRS? Like if you send a chain of commands it it more prone to creating some side effects?
Why would it be an unclear relation?
person.setName("Name").setAge(5).setWeight("30 lbs")...
# It's all a person object (assuming no side effects happen?)
Sorry for asking more questions, I just never thought method chaining can be considered harmful. I can see if chaining would have "side effects", then yes. I would really appreciate if you could give some example of it (if you have time). I wanted to use it for some simple objects that would not cause any side effects.
Just my five cents ...
I don't dislike method chaining, but not for destructive methods (like setters). In NIM it is is nice that I can intermix functional, imperative and OO like programming styles. There is not a huge distinction between using functions and methods. But that has as a consequence that function chaining and method chaining look alike (and are almost the same).
I tend to use chaining in code that is pure functional. Also using it for destructive code (like setters) confuses me when reading the code.
e.g. I could have two versions of the function add (much like your addSimple): a destructive one and a pure functional one.
# destructive (OO like) version:
proc add(var str: string, otherStr: string)
# functional version:
proc add(str: string, otherStr: string): string
When using add it is imediately clear to me which version I'm using and what I can expect from the call.
var s1 = "aa"
#using the destructive version:
s1.add("xx")
s1.add("yy")
#using the functional version:
let s2 = s1.add("xx").add("yy")
Returning the this in every setter (or other destructive method), would force me to search for the definition of the function/method every time, because the calls would look the same. It would also enforce me to use discard all the time when using setters for no good reason.
"Like if you send a chain of commands it it more prone to creating some side effects?"
You do have a side effect: you're changing obj.str. In a functional approach, you would return a new obj with a new str.
What about:
proc badstyle(): int {.discardable.} = 123
proc p(): auto =
badstyle()