No description
Find a file
2021-06-25 03:16:39 +08:00
tests first commit 2021-06-25 03:13:51 +08:00
chain.nim first commit 2021-06-25 03:13:51 +08:00
chain.nimble first commit 2021-06-25 03:13:51 +08:00
license.txt first commit 2021-06-25 03:13:51 +08:00
readme.md modify readme 2021-06-25 03:16:39 +08:00

Chain

For object oriented programming, function chaining and method cascading syntax is really convenient. We already have std/with and cascade. However, when I use them to write real GUI program (for example, wNim), both of them have a lot of limit. So I write this chain macro, which is designed to be drop-in replacement for these two module, and more powerful.

Features:

  • Parameters syntax or block syntax (like std/with).
  • as syntax to create named variable that can be used in block.
  • Expressions will be evaluated only one time (by create anonymous variable).
  • Nested fields, nested calls, and nested chain.
  • Underscore can be used anywhere to represent this object.
  • Works in control flow statements, including block, try/except/finally, defer, if, when, for, while, case, etc.

Notice: using chain wisely can improve readability, but overuse may lead opposite effect.

Basic Usage

Replace std/with:

var x = "yay"
chain x:
  add "abc"
  add "efg"
doAssert x == "yayabcefg"

Replace cascade:

chain Button() as btn: # btn can be used inside and outside the block
  text = "ok"
  width = 30
  color = "#13a89e"
  enable()

or

var btn = chained Button(): # btn cannot be used inside the block
  text = "ok"
  width = 30
  color = "#13a89e"
  enable()

I recommend chain ... as instead of chained because it is more convenient. However, the choice is yours.

Features in detail

  • Parameters syntax.

      var a = 44
      chain(a, +=4, -=5)
      doAssert a == 43
    
      type Point = object
        x, y: int
    
      proc setX(pt: var Point, x: int) = pt.x = x
      proc setY(pt: var Point, y: int) = pt.y = y
    
      chain(Point() as pt, setX(1), setY(2))
      doAssert pt == Point(x: 1, y: 2)
    
  • Block syntax.

      chain 44 as a:
        +=4
        -=5
      doAssert a == 43
    
      chain Point() as pt:
        setX(1)
        setY(2)
      doAssert pt == Point(x: 1, y: 2)
    
  • chain can accept both variable or expression. For expression, unlike std/with, it will be evaluate only one time.

      var count1, count2: int
      template next1(): int = count1.inc; count1
      template next2(): int = count2.inc; count2
      proc nop(x: int) = discard
    
      with next1: # std/with will call next1() three times
        nop; nop; nop
    
      chain next2: # chain will call next2() only one time
        nop; nop; nop
    
      doAssert count1 == 3
      doAssert count2 == 1
    
  • Nested fields, nested calls, and nested chain. In summary, chain add _. to every calls and assignments, except chain, chained, and _(underscore).

      # pseudocode
      chain a:
        b.c.d = e
        f.g.h()
        chain i:
          j.k.l = m
          n.o.p()
    

    To add a expression without _., use parentheses to enclose it.

      chain Point() as pt:
        x = 1
        _.x = 1
        (pt.x = 1)
        # all the same
    
  • Underscore can be used anywhere to represent this object.

      chain "abc":
        (doAssert _ == "abc")
        chain "def":
          (doAssert _ == "def")
    
      chain "abc":
        chain _ & "def":
          &= "ghi"
          _.add _
          _[0] = 'z'
          (doAssert _ == "zbcdefghiabcdefghi")
    
  • Works in control flow statements, including block, try/except/finally, defer, if, when, for, while, case, etc.

      chain 1:
        block:
          +=1
          defer:
            +=1
            try:
              +=1
            finally:
              +=1
              if true:
                +=1
                when true:
                  +=1
                  for i in 1..1:
                    +=1
                    while true:
                      +=1
                      break
    
        (doAssert _ == 9)
    
  • GUI example.

     import wNim/[wApp, wFrame, wMenuBar, wMenu]
     import chain
    
     chain App():
       chain Frame():
         title = "wNim"
         size = (640, 480)
    
         wIdExit do ():
           _.close()
    
         chain MenuBar(_):
           chain Menu(_, "&File"):
             append(wIdOpen, "&Open")
             appendSeparator()
             append(wIdExit, "E&xit")
    
         show()
         center()
    
       mainLoop()
    
    

License

Read license.txt for more details.

Copyright (c) 2021 Kai-Hung Chen, Ward. All rights reserved.