• how to create a simple closure to create validators

    From Mark Summerfield@21:1/5 to All on Thu Jun 20 07:59:24 2024
    I can make a variable that has a specific validator quite easily using
    trace add.

    But I can't work out how to create a validator maker (i.e., a simple
    closure).

    I am using Tcl/Tk 9.0b2.

    Here is the code:

    #!/usr/bin/env tclsh9

    proc valid_percent {varname elemname op} {
    upvar 1 $varname var
    if {$op eq "write"} {
    if {$var < 0} {
    set var 0
    } elseif {$var > 100} {
    set var 100
    }
    }
    }

    proc check {expected actual} {
    if {$expected != $actual} {
    puts "ERROR $expected != $actual"
    } else {
    puts "OK $expected == $actual"
    }
    }

    trace add variable percent write valid_percent

    set percent 50
    check 50 $percent
    set percent -5
    check 0 $percent
    set percent 492
    check 100 $percent

    # All the above works correctly; what's below fails with:
    # can't read "min": no such variable

    proc clamper {min max} {
    proc clamp {varname elemname op} {
    upvar 1 $varname var
    if {$op eq "write"} {
    if {$var < $min} {
    set var $min
    } elseif {$var > $max} {
    set var $max
    }
    }
    }
    return clamp
    }

    trace add variable score write [clamper 0 20]

    set score 15
    check 15 $score
    set score -5
    check 0 $score
    set score 25
    check 20 $score

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From et99@21:1/5 to Mark Summerfield on Thu Jun 20 11:59:38 2024
    On 6/20/2024 12:59 AM, Mark Summerfield wrote:
    I can make a variable that has a specific validator quite easily using
    trace add.

    But I can't work out how to create a validator maker (i.e., a simple closure).

    I am using Tcl/Tk 9.0b2.

    Here is the code:

    #!/usr/bin/env tclsh9

    proc valid_percent {varname elemname op} {
    upvar 1 $varname var
    if {$op eq "write"} {
    if {$var < 0} {
    set var 0
    } elseif {$var > 100} {
    set var 100
    }
    }
    }

    proc check {expected actual} {
    if {$expected != $actual} {
    puts "ERROR $expected != $actual"
    } else {
    puts "OK $expected == $actual"
    }
    }

    trace add variable percent write valid_percent

    set percent 50
    check 50 $percent
    set percent -5
    check 0 $percent
    set percent 492
    check 100 $percent

    # All the above works correctly; what's below fails with:
    # can't read "min": no such variable

    proc clamper {min max} {
    proc clamp {varname elemname op} {
    upvar 1 $varname var
    if {$op eq "write"} {
    if {$var < $min} {
    set var $min
    } elseif {$var > $max} {
    set var $max
    }
    }
    }
    return clamp
    }

    trace add variable score write [clamper 0 20]

    set score 15
    check 15 $score
    set score -5
    check 0 $score
    set score 25
    check 20 $score


    Here's how I might do this:


    proc validateRange {min max name1 name2 op} {
    if { $name2 eq "" } { ;# scalar
    upvar 1 $name1 newValue
    if { $newValue < $min } {
    set newValue $min
    } elseif { $newValue > $max } {
    set newValue $max
    }
    } else { ;# array - left as exercise for reader

    }
    }

    trace add variable my_percent write {validateRange 0 20 }

    set my_percent 10
    puts "my_percent= |$my_percent| was 10"
    set my_percent 50
    puts "my_percent= |$my_percent| was 50"
    set my_percent -50
    puts "my_percent= |$my_percent| was -50"


    trace add variable my_percent100 write {validateRange 5 100 }

    set my_percent100 10
    puts "my_percent100= |$my_percent100| was 10"
    set my_percent100 500
    puts "my_percent100= |$my_percent100| was 500"
    set my_percent100 -50
    puts "my_percent100= |$my_percent100| was -50"

    output:

    my_percent= |10| was 10
    my_percent= |20| was 50
    my_percent= |0| was -50
    my_percent100= |10| was 10
    my_percent100= |100| was 500
    my_percent100= |5| was -50

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Joerg Mertens@21:1/5 to Mark Summerfield on Thu Jun 20 21:48:16 2024
    Mark Summerfield <mark@qtrac.eu> wrote:

    proc clamper {min max} {
    proc clamp {varname elemname op} {
    upvar 1 $varname var
    if {$op eq "write"} {
    if {$var < $min} {
    set var $min
    } elseif {$var > $max} {
    set var $max
    }
    }
    }
    return clamp
    }

    To add to what others have said: Tcl doesn't have nested procedures.
    "clamp" will just be another toplevel procedure, so it won't have
    access to the arguments of "clamper". It also means, that "clamp"
    will be overwritten with every call to "clamper".

    There are also no lexical scopes inside of procedures and therefore
    no lexical closures. So most Lisp patterns won't work here.

    Regards

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mark Summerfield@21:1/5 to All on Fri Jun 21 09:43:00 2024
    On Thu, 20 Jun 2024 11:59:38 -0700, et99 wrote:

    On 6/20/2024 12:59 AM, Mark Summerfield wrote:
    I can make a variable that has a specific validator quite easily using
    trace add.

    But I can't work out how to create a validator maker (i.e., a simple
    closure).

    I am using Tcl/Tk 9.0b2.

    Here is the code:

    #!/usr/bin/env tclsh9

    proc valid_percent {varname elemname op} {
    upvar 1 $varname var if {$op eq "write"} {
    if {$var < 0} {
    set var 0
    } elseif {$var > 100} {
    set var 100
    }
    }
    }

    proc check {expected actual} {
    if {$expected != $actual} {
    puts "ERROR $expected != $actual"
    } else {
    puts "OK $expected == $actual"
    }
    }

    trace add variable percent write valid_percent

    set percent 50 check 50 $percent set percent -5 check 0 $percent set
    percent 492 check 100 $percent

    # All the above works correctly; what's below fails with:
    # can't read "min": no such variable

    proc clamper {min max} {
    proc clamp {varname elemname op} {
    upvar 1 $varname var if {$op eq "write"} {
    if {$var < $min} {
    set var $min
    } elseif {$var > $max} {
    set var $max
    }
    }
    }
    return clamp
    }

    trace add variable score write [clamper 0 20]

    set score 15 check 15 $score set score -5 check 0 $score set score 25
    check 20 $score


    Here's how I might do this:


    proc validateRange {min max name1 name2 op} {
    if { $name2 eq "" } { ;# scalar
    upvar 1 $name1 newValue if { $newValue < $min } {
    set newValue $min
    } elseif { $newValue > $max } {
    set newValue $max
    }
    } else { ;# array - left as exercise for reader

    }
    }

    trace add variable my_percent write {validateRange 0 20 }

    set my_percent 10 puts "my_percent= |$my_percent| was 10"
    set my_percent 50 puts "my_percent= |$my_percent| was 50"
    set my_percent -50 puts "my_percent= |$my_percent| was -50"


    trace add variable my_percent100 write {validateRange 5 100 }

    set my_percent100 10 puts "my_percent100= |$my_percent100| was 10"
    set my_percent100 500 puts "my_percent100= |$my_percent100| was 500"
    set my_percent100 -50 puts "my_percent100= |$my_percent100| was -50"

    output:

    my_percent= |10| was 10 my_percent= |20| was 50 my_percent= |0| was -50 my_percent100= |10| was 10 my_percent100= |100| was 500 my_percent100=
    |5| was -50

    Ah, get that, v. clever. In the end I used this simple variation:

    proc clamp {min max varname elemname op} {
    upvar 1 $varname var
    if {$op eq "write"} {
    if {$var < $min} {
    set var $min
    } elseif {$var > $max} {
    set var $max
    }
    }
    }

    trace add variable score write {clamp 0 20}


    Thank you.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)