Sunday, December 1, 2024

Java Generic Wildcards

this text is best read in a fixed width terminal 


C++ compiles code for each required parameterized type
Java compiles only one instance of the parameterized type, using Object as the type

because generic types in java erase information on type
  and still the type must be verified by the compiler
  there are cases when the code should compile but it doesn't
  the solution was to relax type checks at compile time


the '?' wildcard can only appear on the left hand side of an assignment
    and only when the variable or parameter is defined
  it imposes constraints on the right hand side of the assignment

a variable of <? extends MyClass> type can only refer objects of types that inherit MyClass
    this sets an upper bound
  you can't write to it because
    the compiler can't ensure that what you write is a derivate of its current content
    and therefore it cannot enforce homogeneity (?!)
  you can read <? super MyClass>'s from it
  this variable is a producer

a variable of <? super MyClass> type can only refer objects of types that are inherited by MyClass
    this sets a lower bound
  you can write <? extends MyClass>'s to it
  you can only read in Object's from it because the compiler only knows that it is a derivate of Object
  this variable is a consumer

variables of <?> type can refer objects of any type
  you can not write to it because
    the compiler can't ensure that what you write is a derivate of its current content
    and therefore it cannot enforce homogeneity (?!)
  you can only read in Object's from it because the compiler only knows that it is a derivate of Object

these constraints are verified at compile time


class Wild {
  public static void main(String args[]) {

    /*
    all errors are compile time

    as long as the type of right hand side in the variable declaration
        is compatible with the variable type,
      its type does not influence future compatibilities of the variable
      because its type in the assigned variable is not known to the compiler at the compile time

    of course, anything can be done with explicit type casts
    */


    MyGen<? extends MyClass> ge = new MyGen<MyClass>(new MyClass(1, 2));
      // ge = MyGen, extends
    // ge.e cannot be written but it can be read
    //   ge.e cannot be written because it may hold any class equal to or below MyClass
    //   and they need to ensure ge.e's homogeneity (?!)
    // ge.e is a producer
    // ge.e = new MyBase(3);
      // ByBase cannot be converted to CAP#1
      //   CAP#1 extends MyClass from capture of ? extends MyClass
    // ge.e = new MyClass(4, 5);
      // MyClass cannot be converted to CAP#1
      //   CAP#1 extends MyClass from capture of ? extends MyClass
    // ge.e = new MyDeriv(6, 7, 8);
      // MyDeriv cannot be converted to CAP#1
      //   CAP#1 extends MyClass from capture of ? extends MyClass
    MyBase geb = ge.e;
    MyClass gec = ge.e;
    // MyDeriv ged = ge.e;
      // incompatible types, ge.e can have any type equal or below MyClass
    System.out.println("ge.e = " + ge.e + "\t\tge.e.i = " + ge.e.i + "\tge.e.j = " + ge.e.j);


    MyGen<? super MyClass> gs = new MyGen<MyClass>(new MyClass(1, 2));
      // gs = MyGen, super
    // gs.e can be written but it can be read only into an Object
    //   gs.e can be read only into an Object because it may hold any class equal or above MyClass
    // gs.e is a consumer
    // gs.e = new MyBase(3);
      // incompatible types, gs.e can have any type equal to or above MyClass
    gs.e = new MyClass(4, 5);
    gs.e = new MyDeriv(6, 7, 8);
    // MyBase gsb = gs.e;
      // CAP#1 cannot be converted to MyBase
      //   CAP#1 extends Object super: MyClass from capture of ? super MyClass
    // MyClass gsc = gs.e;
      // CAP#1 cannot be converted to MyClass
      //   CAP#1 extends Object super: MyClass from capture of ? super MyClass
    // MyDeriv gsd = gs.e;
      // CAP#1 cannot be converted to MyDeriv
      //   CAP#1 extends Object super: MyClass from capture of ? super MyClass
    Object gso = gs.e;
    // the type of gs.e is known at run time
    System.out.println("gs.e class is: " + gs.e.getClass().getName());
      // gs.e class is: MyDeriv
    // but not at compile time, when gs.e is only known to be of type Object
    // System.out.println("gs.e = " + gs.e + "\t\tgs.e.i = " + gs.e.i + "\tgs.e.j = " + gs.e.j +
    //                      "\tgs.e.k = " + gs.e.k);
      // cannot find symbols gs.e.i, gs.e.j and gs.e.k


    MyGen<?> g = new MyGen<MyClass>(new MyClass(1, 2));
    // g = MyGen
    // we know nothing about the type of g
    //   so the only allowed operation is to read it into an Object
    // g.e = new MyBase(3);
      // MyBase cannot be converted to CAP#1
      //   CAP#1 extends Object from capture of ?
    // g.e = new MyClass(4, 5);
      // MyClass cannot be converted to CAP#1
      //   CAP#1 extends Object from capture of ?
    // g.e = new MyDeriv(6, 7, 8);
      // MyDeriv cannot be converted to CAP#1
      //   CAP#1 extends Object from capture of ?
    // MyBase gb = g.e;
      // CAP#1 cannot be converted to MyBase
      //   CAP#1 extends Object from capture of ?
    // MyClass gc = g.e;
      // CAP#1 cannot be converted to MyClass
      //   CAP#1 extends Object from capture of ?
    // MyDeriv gd = g.e;
      // CAP#1 cannot be converted to MyDeriv
      //   CAP#1 extends Object from capture of ?
    Object go = g.e;
    // System.out.println("g.e = " + g.e + "\t\tg.e.i = " + g.e.i + "\tg.e.j = " + g.e.j);
      // cannot find symbols g.e.i and g.e.j
  }
}


// my generic class: it stores an object of a generic type
class MyGen<T> {
  T e;
  // element
  MyGen(T e) {
    this.e = e;
  }
}

class MyBase {
  int i;
  MyBase(int i) {
    this.i = i;
  }
}

class MyClass extends MyBase {
  int j;
  MyClass(int i, int j) {
    super(i);
    this.j = j;
  }
}

class MyDeriv extends MyClass {
  int k;
  MyDeriv(int i, int j, int k) {
    super(i, j);
    this.k = k;
  }
}

Saturday, February 24, 2024

Tcl socket example explained


# the source of the example code: https://www.tcl.tk/man/tcl8.5/tutorial/Tcl42.html

# paste it in a fixed width character terminal for better reading 


proc serverOpen {channel addr port} {
    puts "serverOpen: channel: $channel - from Address: $addr  Port: $port"
    puts "The default state for blocking is: [fconfigure $channel -blocking]"
    puts "The default buffer size is: [fconfigure $channel -buffersize ]"

    # Set this channel to be non-blocking.
    fconfigure $channel -blocking 0
    set bl [fconfigure $channel -blocking]
    puts "After fconfigure the state for blocking is: $bl"
 
    # Change the buffer size to be smaller
    fconfigure $channel -buffersize 12
    puts "After fconfigure buffer size is: [fconfigure $channel -buffersize ]\n"

    # When input is available, read it.
    # the script is executed at the (global level)
    #   in the interpreter in which the fileevent command was invoked
    #   the file event handler is deleted if it ever returns an error (man fileevent)
    # the event is triggered and the script is run only when the first data comes in
    # 'Server' is just a name, such that readLine can be used in other contexts, too
    fileevent $channel readable "readLine Server $channel"
}

proc readLine {who channel} {
    global didRead
    global blocked

    puts "readLine: There is input for $who on $channel"

    # on the first iteration of the while loop below there is no 'eol',
    #   the channel is in non-blocking mode, so the read fails and nothing is read, see 'man 3tcl gets'
    # on the second iteration of the 'while' loop below, this does succeed
    set len [gets $channel line]
    # the channel is non-blocking, but it may be "blocked", see 'man fblocked'
    # 1 if blocked, 0 if not blocked
    set blocked [fblocked $channel]
    puts "Characters Read: $len  Fblocked: $blocked"

    # -1 is not enough data, see 'man 3tcl gets'
    if {$len < 0} {
        if {$blocked} {
            # not enough to read
            puts "Input is blocked"
        } else {
            # the 'eof' case
            puts "The socket was closed - closing my end"
            # this closes the new channel created by 'socket -server' for the new connection
            #   the server itself is closed in the last line of the script
            close $channel;
        }
    } else {
        puts "Read $len characters:  $line"
        puts $channel "This is a return"
        flush $channel;
    }
    incr didRead;
    # puts "readLine: didRead = $didRead"
}

set server [socket -server serverOpen 33000]

# after 120 update;    # This kicks MS-Windows machines for this application

# no call for serverOpen here
# puts {before the first vwait}
# vwait didRead
# puts {after the first vwait}

# puts {before socket client}

# this sends a connection request to the server listening on port 33000
#   the server creates a (new channel) on a new port especially for this connection
#   and then it makes the client connect to this new channel
# also now the server triggers an event that will run serverOpen when processed
set sock [socket 127.0.0.1 33000]

# puts {after socket client}

# if the first vwait is commented, then serverOpen is called here
# puts {before the second vwait}
# vwait didRead
# puts {after the second vwait}

set bl [fconfigure $sock -blocking]
set bu [fconfigure $sock -buffersize]
puts "Original setting for sock: Sock blocking: $bl buffersize: $bu"

fconfigure $sock -blocking No
fconfigure $sock -buffersize 8;

set bl [fconfigure $sock -blocking]
set bu [fconfigure $sock -buffersize]
puts "Modified setting for sock: Sock blocking: $bl buffersize: $bu\n"

# Send a line to the server -- NOTE flush
set didRead 0
# this makes fileevent to trigger an event that calls lineRead when processed
puts -nonewline $sock "A Test Line"
flush $sock;

# if the first two vwait's are commented, then both serverOpen and readLine are called here
# puts {before the third vwait}
# vwait didRead
# puts {after the third vwait}

# this sends "NewLine\n" immediately after "Read 18 characters:  A Test Line"
# readLine is run only on 'wait didRead', which reads the fileevent event
#   so this iterates two times
while {$didRead < 2} {
    puts "while: didRead = $didRead"
    # Wait for didRead to be set
    vwait didRead
    if {$blocked} {
        puts $sock "Newline"
        flush $sock
        puts "SEND NEWLINE"
    }
}

# this reads the readLine "This is a return" message
set len [gets $sock line]
puts "Return line: $len -- $line"
# this closes the client socket
close $sock
# puts {before last vwait}
# this processes the last fileevent event that calls readLine for the last time
#   now readLine closes the new channel because it sees that it is closed on the client side
vwait didRead
# this closes the server, which is still listening on the 30000 port
catch {close $server}
# so 'socket' creates and manages the connection
#   similarly to a pipe or a fifo (see ~/linux/misc/bash/fifo.txt)

# 'man 3tcl gets' (three cases)
# the three non-blocking cases are:
#   successfully read
#   not enough data, returns -1 and it's blocked
#   'eof', returns -1 and it's not blocked


Friday, February 23, 2024

Tcl fileevent events explained

# paste it in a fixed width character terminal for better reading 

# do '$ touch test.txt' before you run it with '$ tclsh scriptname.tcl'

set f [open test.txt]
puts "f = $f"

set k 0
after 0 {puts "before: k = $k"}
# k is in the global space, the script is run in the global space
fileevent $f readable {incr k; puts "k = $k"}
after 500
# events are processed in the order in which they have been created
#   'vwait k' stops processing events when it finds the first k modification event
# one new fileevent event is created when fileevent is first called
#   and it is PERMANENTLY placed FIRST IN LINE in the event queue
#   this event probably creates a hook for vwait and update such that
#     two more fileevent events are created next time when
#       either the event loop is entered and there is no such a fileevent event first in the queue
#       or the queue is empty when the event loop is exited
#       these two fileevent events are placed normally last in line in the event queue
#     such a scenario is also suggested by
#       "To generate events in case of an empty queue
#       Tcl_DoOneEvent will first ask all registered event sources to setup themselves"
#       at https://tcl.sourceforge.net/c-api/notifier.html
#   error bug this is crazy and that's why it's not mentioned in man fileevent
for {set j 1} {$j<=6} {incr j} {
  puts {new for cycle}
  after 500
  after 0 {puts "before: k = $k"}
  after 500
  # update
  vwait k
  }

# update

close $f
puts "f = $f"


Sunday, January 21, 2024

Tcl 'oo::define self' explained



oo::class create c {

method a {} { puts "c a" }

self method b {} { puts "c b" }}




c create o




# this raises unknown method "a", because 'a' is a member of 'c' as a class, not as an object

# c a




# this works, because 'b' is a member of 'c' as an object, due to self, see man oo_define (^ *self)

c b




# this works, because 'a' is a member of the class 'c' that created o

o a




# this raises unknown method "b", because b is a member of c as an object, not as a class

# o b