Version 8 of list ensemble

Updated 2018-07-24 18:35:12 by bll

list ensemble

bll 2018-7-24: An exercise in learning about namespace ensemble.

This is an implementation (one possible anyways) of the list ensemble command. All of the usual list commands are still present. List creation is backwards compatible by using the namespace ensemble -unknown handler.

It seems to work, but has not been tested extensively. I had a lot of trouble creating this. namespace ensemble seems to be very sensitive. Many things I tried did not work as expected from reading the wiki page on namespace ensemble. The documentation could use some updating to be more precise and could use some quality examples.

 First pass
#!/usr/bin/tclsh
#
# Originally written by Brad Lanam
# In the public domain
#

rename ::list ::_list

namespace eval ::list {
  set cmdlist [::_list append assign index insert length map range]
  ::lappend cmdlist repeat replace reverse search set sort
  ::lappend cmdlist create size
  namespace export {*}$cmdlist
  namespace ensemble create \
      -unknown ::list::_unknown \
      -subcommands $cmdlist
  # I also tried -map {size length}, but that did not work out.  
  # I suppose there must be a way to get -map to work, but I could not find it.

  variable _cflag false
  variable _csave {}

  proc _unknown { args } {
    variable _cflag
    variable _csave

    # Unfortunately, the unknown handler is very specific to
    # a namespace ensemble and is not a general handler.
    # argument index 1 is stripped out and lost.
    # Have to jump through some hoops here.
    # There's a lot of weirdness here
    # " If the -unknown handler returns anything else,
    #   it is interpreted as a command prefix"
    # does not seem to be true.  'return create' does not work,
    # and in fact, 'return create' fails to pass the appended argument list,
    # the create routine gets called, and errors out with a bad
    # subcommand error.

    # save the needed value for the create command
    set _csave [::lindex $args 1]
    set _cflag true
    return [::_list ::list create]
  }

  proc create { args } {
    variable _cflag
    variable _csave

    if { $_cflag } {
      # more weirdness, cannot seem to set and use local variables, or
      # even global variables.
      # I tried:
      #   set val [::_list $_csave {*}$args]
      #   set _csave {}
      #   return $val
      # and it did not work.
      # Tried:
      #   variable _tval
      #   set _tval [::_list $_csave {*}$args]
      #   set _csave {}
      #   return $_tval
      # and it did not work.
      set _cflag false
      return [::_list $_csave {*}$args]
    }
    return [::_list {*}$args]
  }
  # for orthogonality...
  proc size { args } {
    return [::llength {*}$args]
  }

  proc append { args } {
    return [uplevel 1 ::lappend {*}$args]
  }
  proc assign { args } {
    return [uplevel 1 ::lassign {*}$args]
  }
  proc index { args } {
    return [::lindex {*}$args]
  }
  proc insert { args } {
    return [::linsert {*}$args]
  }
  proc length { args } {
    return [::llength {*}$args]
  }
  proc map { args } {
    return [uplevel 1 ::lmap {*}$args]
  }
  proc range { args } {
    return [::lrange {*}$args]
  }
  proc repeat { args } {
    return [::lrepeat {*}$args]
  }
  proc replace { args } {
    return [::lreplace {*}$args]
  }
  proc reverse { args } {
    return [::lreverse {*}$args]
  }
  proc search { args } {
    return [::lsearch {*}$args]
  }
  proc set { args } {
    return [uplevel 1 ::lset {*}$args]
  }
  proc sort { args } {
    return [::lsort {*}{$args}]
  }
}

package provide listensemble 0.1

PL 2018-07-24: You can make use of ensemble create -map like this:

namespace eval list {
    namespace export {[a-z]*}
    namespace ensemble create -map {
        create  ::_list
        size    ::llength
        append  ::lappend
        assign  ::lassign
        index   ::lindex
        insert  ::linsert
        length  ::llength
        map     ::lmap
        range   ::lrange
        repeat  ::lrepeat
        replace ::lreplace
        reverse ::lreverse
        search  ::lsearch
        set     ::lset
        sort    ::lsort
        each    ::foreach
    }
}

bll 2018-7-24: Thanks for that. That simplifies things. I think I was trying to -map on to routines internal to the namespace (in different ways), and it wasn't working.

This simplifies things. <strikeout>It appears namespace ensemble runs the -map'd commands in the correct frame.</strikeout>

I tried the 'each', but that doesn't work. Perhaps the syntax to use it eludes me.

join is also debatable, but I'm just going to leave this as the explicit list commands.

Not sure how to format this wiki page at this time, perhaps after all the discussion is done, the final form can be displayed up top, and roll the rest into a discussion block.

 Second pass
#!/usr/bin/tclsh
#
# Originally written by Brad Lanam, Peter Lewerin
# In the public domain
#

rename ::list ::_list

namespace eval ::list {
  namespace export {[a-z]*}
  # size is just added for orthogonality purposes
  namespace ensemble create \
      -unknown ::list::_unknown \
      -subcommands create \
      -map {
          size    ::llength
          append  ::lappend
          assign  ::lassign
          index   ::lindex
          insert  ::linsert
          length  ::llength
          map     ::lmap
          range   ::lrange
          repeat  ::lrepeat
          replace ::lreplace
          reverse ::lreverse
          search  ::lsearch
          set     ::lset
          sort    ::lsort
      }


  variable _cflag false
  variable _csave {}

  proc _unknown { args } {
    variable _cflag
    variable _csave

    # Unfortunately, the unknown handler is very specific to
    # a namespace ensemble and is not a general handler.
    # argument index 1 is stripped out and lost (though it
    # is available initially.
    # Have to jump through some hoops here.
    #
    # There's a lot of weirdness here...
    # " If the -unknown handler returns anything else,
    #   it is interpreted as a command prefix"
    # does not seem to be true.  'return create' does not work,
    # and in fact, 'return create' fails to pass the appended argument list,
    # the create routine gets called, and errors out with a bad
    # subcommand error.
    #
    # I suspect this is just a documentation error, where
    # 'command prefix' should just read 'command'.

    # save the needed value for the create command
    set _csave [::lindex $args 1]
    set _cflag true
    return [::_list ::list create]
  }

  proc create { args } {
    variable _cflag
    variable _csave
    if { $_cflag } {
      # more weirdness, cannot seem to set and use local variables, or
      # even global variables.
      # I tried:
      #   set val [::_list $_csave {*}$args]
      #   set _csave {}
      #   return $val
      # and it did not work.
      # Tried:
      #   variable _tval
      #   set _tval [::_list $_csave {*}$args]
      #   set _csave {}
      #   return $_tval
      # and it did not work.
      set _cflag false
      return [::_list $_csave {*}$args]
    }
    return [::_list {*}$args]
  }
}

package provide listensemble 0.2

bll 2018-7-24: I was wrong. Executed in the wrong frame. Now that I have tested properly...

#!/usr/bin/tclsh
#
# Originally written by Brad Lanam, Peter Lewerin
# In the public domain
#

rename ::list ::_list

namespace eval ::list {
  namespace export {[a-z]*}
  # size is just added for orthogonality purposes
  namespace ensemble create \
      -unknown ::list::_unknown \
      -subcommands {create append assign map set} \
      -map {
          size    ::llength
          index   ::lindex
          insert  ::linsert
          length  ::llength
          range   ::lrange
          repeat  ::lrepeat
          replace ::lreplace
          reverse ::lreverse
          search  ::lsearch
          sort    ::lsort
      }


  variable _cflag false
  variable _csave {}

  proc _unknown { args } {
    variable _cflag
    variable _csave

    # Unfortunately, the unknown handler is very specific to
    # a namespace ensemble and is not a general handler.
    # argument index 1 is stripped out and lost (though it
    # is available initially.
    # Have to jump through some hoops here.
    #
    # There's a lot of weirdness here...
    # " If the -unknown handler returns anything else,
    #   it is interpreted as a command prefix"
    # does not seem to be true.  'return create' does not work,
    # and in fact, 'return create' fails to pass the appended argument list,
    # the create routine gets called, and errors out with a bad
    # subcommand error.
    #
    # I suspect this is just a documentation error, where
    # 'command prefix' should just read 'command'.

    # save the needed value for the create command
    set _csave [::lindex $args 1]
    set _cflag true
    return [::_list ::list create]
  }

  proc create { args } {
    variable _cflag
    variable _csave
    if { $_cflag } {
      # more weirdness, cannot seem to set and use local variables, or
      # even global variables.
      # I tried:
      #   set val [::_list $_csave {*}$args]
      #   set _csave {}
      #   return $val
      # and it did not work.
      # Tried:
      #   variable _tval
      #   set _tval [::_list $_csave {*}$args]
      #   set _csave {}
      #   return $_tval
      # and it did not work.
      set _cflag false
      return [::_list $_csave {*}$args]
    }
    return [::_list {*}$args]
  }

  proc append { args } {
    return [uplevel 1 ::lappend {*}$args]
  }
  proc assign { args } {
    return [uplevel 1 ::lassign {*}$args]
  }
  proc map { args } {
    return [uplevel 1 ::lmap {*}$args]
  }
  proc set { args } {
    return [uplevel 1 ::lset {*}$args]
  }
}

package provide listensemble 0.3