Thursday, December 18, 2014

7L7W Week 2: Io Day 2 round 2 continued>Toddling

I should probably check to see if something has defined the sum slot on Number and List first, but... let's start this simple.

Number sum := method(call target)
List sum := method(
total := 0;
foreach(val, total = total + val sum)
)

Io> a := list(list(1, 2, 3), list(1, 2, 3))
==> list(list(1, 2, 3), list(1, 2, 3))
Io> a sum
==> 12

As the title suggests, I think I'm getting used to this now. This may not be exactly how it should be done, but it was ridiculously easy to implement (even with accidental recursion >.> ). Implementing a sum method on two base prototypes of the language leaves me a touch uneasy but these are additions to the prototypes rather than redefinitions. If I were going to try this in production code of some sort I would need to either use a less generic slot name or properly check for an existing sum slot on both prototypes.
... Also it strikes me that the question was to write a program, not a method on the List prototype. Oh well. Nobody is grading this but me.

The averaging problem was pretty straightforward but I had some confusion around the select message. Mostly because I saw a reference in the programming guide to a method ISNUMBER() which doesn't appear to actually exist anywhere? Or at any rate I couldn't find it so I had to do this:

List myAverage := method(
values := call target select(v, v type == "Number");
if(values size == 0, Exception raise("No Number in target List"));
values reduce(+) / values size
)

Unlike the summing problem this solution will not handle multidimensional arrays. I could swap the reduce message for the sum method defined above but I'd have to handle n-dimensionality in the select message at the top of the method as well.

... I was going to finish day 2 today and post this. Actually I was hoping to post this yesterday but it has not been a very good week for side projects. Unfortunately it's going to have to wait. It might have to wait until the weekend. I want to get this posted at least and I'll try to get day 2 finished before Monday.

Tuesday, December 16, 2014

7L7W Week 2: Io Day 2 round 2>Finding my feet

I might be getting the hang of this.
recursiveFibonacci := method(n, 
if(n == 0, 
0,
if(n == 1,
1,
recursiveFibonacci(n - 1) + recursiveFibonacci(n - 2)
)
)
)

loopedFibonacci := method(n,
nSub1 := 0;
currentN := 1;
for(i, 1, n, 
currentN = currentN + nSub1;
nSub1 = if(i == 1, 0, currentN - nSub1))
currentN
)

Number recursiveFibonacci := method( 
if(call target == 0, 
0,
if(call target == 1,
1,
((call target - 1) recursiveFibonacci) + ((call target - 2) recursiveFibonacci)
)
)
)

Number loopedFibonacci := method(
nSub1 := 0;
currentN := 1;
for(i, 1, call target, 
currentN = currentN + nSub1;
nSub1 = if(i == 1, 0, currentN - nSub1))
currentN
)

Do I get double bonus points? Hm, no, probably not. It's not exactly a stretch to move from a parameter to calling the message target is it? These seem pretty self explanatory to me (Fib numbers aren't a new science after all) but I can't help feeling like it could be done better.

Division by zero? Sure, let's break basic math... >.<

Number divide := Number getSlot("/")
Number / = method(n,
if(n == 0,
0,
call target divide(n)
)
)

I did cheat a bit. StackOverflow pointed me in the right direction.

And it's bedtime now. I'll get the rest of the self study done tomorrow (with a grain of salt). So progress has definitely slowed in my 7(3!) week journey. However it's also getting more interesting so assuming there's a correlation between difficulty and interest... yeah.
Then again it may be due to my lack of cohesive sleep schedule (have I mentioned lately that ridiculous software installs suck?) which sadly will only get worse. There's another install this weekend (end of year compliance, yippee) then a family vacation (meaning all my wife's immediate family, which is neither vacation nor particularly conducive to any kind of study) right after Christmas... which is itself a disruption. Then of course there's the end of the year, which is another all-nighter (I've really got to streamline the end of month process)... I probably could have started this at a better time of year. Then again interrupted progress is better than sitting around playing video games or trying to study something that has gotten stale and boring. Maybe I'll take a break from 7L7W after week 2 and get back to some C#. Play the flipflop game with them. Or maybe not. Since I'm playing dablling all over the place maybe I'll crack that LISP book back open. I'm not sure the original  rationale for shelving it still holds. Ah, the possibilities.

Also, the song that's been driving me today like Paul Revere on speed. That's good stuff there. I must recant my uninformed, juvenile opinions of rap. I clearly had an insufficient sample size to base opinion on.

Monday, December 15, 2014

Google ate my organization

Damnit Google. The whole world may be going to flat, no order, bullshit but I personally keep my files organized in folders. I don't want to search every single bloody time I want to open something. I know right the fuck where it is, let me open it.

Or, I would know if you hadn't moved everything to this worthless damned timeline BS. Give me back my folders; I don't want to wait for you to load a Facebook-alike 'productivity' interface just so I can click through it every single time I try to open something. Talk about a waste of lifespan.

7L7W Week 2: Io Day X>Digging in

Feeling almost human again after my weekend. Serious 2 week prep & 4 hour imp software updates suck. Moving right along.

Let me try to break this down.

Io> test := method(stuff, stuff println)
==> method(stuff,
    stuff println
)
Io> test("Hello World")
Hello World
==> Hello World
Io> 

Interesting things are happening here. First I'm creating a slot in the Lobby...
Io> Lobby slotSummary
==>  Object_0x5bc8c0:
  Lobby            = Object_0x5bc8c0
  Protos           = Object_0x5bc860
  _                = "Hello World"
  exit             = method(...)
  forward          = method(...)
  set_             = method(...)
  test             = method(stuff, ...)

Io>

called test. But... I'm already ahead of myself. >.<

The method() block is sent as a message to the := assignment operator which becomes setSlot("test", method ... ) via a macro creating a slot in the Lobby and loads the method message into it. setSlot()sends the method block as a message, I think, and so the Lobby outputs the method with ==>.

In the next line I craft a message test and give it the string "Hello World" as an argument. This message is sent to the Lobby, where everything eventually lands I guess? It's the default evaluation context when there's no other object to catch the message. The Lobby responds to the test message with its test slot.

The Lobby's test slot is a method block that sends the println message to the first message argument (any additional arguments are ignored and generate no errors that I can see, coder beware). So a println message is sent to my "Hello World" object causing it to print to output. The "Hello World" message also gets sent on to the Lobby which outputs it with ==>.

That's a lot to follow in two lines of code. Time to take another hack at day 2.
Point of interest: the Lobby slot _ seems to contain whatever message was last output by Lobby via ==>. I could stand to learn the terminology for that. But I wonder how long it would take to crash the runtime by repeatedly sending Lobby slotSummary >.>

Friday, December 12, 2014

7L7W Week 2(early): Io Day 2>Empty tank

I followed the day's section but I didn't internalize it. Something is missing in my understanding and I don't have enough energy left in me today to chase it down. I'll have to go over it again tomorrow, assuming I get a chance to.

I like Io. It's different in a way that demands that I reshape the way my thoughts flow. In the same way that learning to use Suprtool for ETL processing required a more-than-evolutionary change in thinking from using SQL-like languages. I enjoy twisting my brain into different shapes like this.

Thursday, December 11, 2014

7L7W Week 2(early): Io Day 1>Scrumptious

Now this, this is something that looks exciting on day1. Yes, it's actually even easier to blow your foot off with a single line of code than in Ruby (Object clone := "hosed" indeed!) but for that only slighter greater power you get an incredibly flexible language. The syntax is extremely small and there seems to be only a very limited number of concepts to grok. No doubt the libraries are an even bigger elephant to devour than in most languages, or at any rate an even higher priority, simply because Io doesn't seem to have much in the way of plumbing available in the base language (file handling? don't see it yet) Even so... I think I'll spend some time with this one and see how it fits. Probably after this 7 weeks course is done in... oh at this rate about 3 weeks.

Enough prologue. Io is ridiculously tricky to search for on Google so I'm dropping links to my 'Find' results.

  • Examples: 
  • Community: There's an official mailing list, twitter, and IRC channel. No idea how active they are. Otherwise, Stack Overflow is all I could find.
  • Style guide: This wikibooks entry looks decent. No idea if there's anything more canonical. I'm not terribly perturbed about style right at the moment though. If I stay in Io long enough to have to find more resources I expect I'll find style canon alongside those searches.
Amusingly, many of my searches related to Io have turned up blogs of people, like me, working and blogging their way through this book. I find that quite amusing, though it highlights an apparent lack of external resources.

Anyway. On with the show. 'Answers'
  • 1+1 runs, 1+"one" does not. 1 is a Number type and "one" is a Sequence type. Sequences don't respond to the + operator. On the other hand, an object can be assigned to any other object at will and according to the Io programming guide even inheritance can be changed at runtime. Which, it seems, would make Io very weakly typed.
  • Everything is true. The cake is always honest. Unless cake := false clone or cake := nil clone. For proofs:
    • true and 0
      • ==> true
    • true and ""
      • ==> true
    • true and nil
      • ==> false
    • true and false
      • ==> false
  • <Object> slotNames or perhaps <Object> proto slotNames. You can also send the slotSummary message to an object if you also want to know what's in each slot.
  • = is assignment to a slot. := creates and assigns a slot (no unassigned variables, ever?) ::= creates and assigns a slot and defines an accessor method set<slotname>() in the object where <slotname> is the name of the slot with an upshifted first letter (existing catpitals are unchanged, only the first letter is upshifted).

So. Do:
  • doFile("filename") filesystem context is where the Io binary is executed from, rather than where it resides. This can also be sent like any other message to an object.
  • Execute the code in a slot given its name... I interpret that as 'how do you eval() a string'. Which is as so:
Bond := Object clone
Bond martini ::= nil
Bond orderMartini := "setMartini(\"shaken, not stirred\")"
Bond slotSummary
Bond doString(Bond orderMartini)
Of course he might just have meant Bond setMartini("shaken, not stirred") or in words, send the slot name as a message to the object in order to execute the code in the slot. In fact, that's probably the simple answer that was being looked for. I tend to overcomplicate >.>

So far so good. I'm quite a bit more excited by Io than Ruby on D1.

Wednesday, December 10, 2014

7L7W Week 1: Ruby Day 3>40 ct rubies

Hm. Only 3 days? That's not a week; things were just getting interesting.
module ActsAsCsv
  def self.included(base)
    base.extend ClassMethods
  end
  
  module ClassMethods
    def acts_as_csv
      include InstanceMethods
    end
  end
  
  module InstanceMethods
    attr_accessor :headers, :csv_contents

    def initialize
      read
    end

    def read
      @csv_contents = []
      file = File.new(self.class.to_s.downcase + '.txt')
      @headers = file.gets.chomp.split(', ')
    
      file.each do |row|
        @csv_contents << row.chomp.split(', ')
      end
    end
    
    def each
      @csv_contents.each do |row|
        yield(CsvRow.new(@headers, row))
      end
    end

  end
end

class CsvRow
  def initialize(headers, row)
    @data = Hash[headers.zip(row)]
  end
  
  def method_missing name, *args
    puts @data[name.to_s]
  end
end

class RubyCsv
  include ActsAsCsv
  acts_as_csv
end

m = RubyCsv.new
puts m.headers.inspect
puts m.csv_contents.inspect
puts "============================"

class Day3Csv
  include ActsAsCsv
  acts_as_csv
end

m = Day3Csv.new
puts m.headers.inspect
puts m.csv_contents.inspect
puts "----------------------------"

m.each {|row| puts row.one}

It doesn't look like much (and my additions to the book's template are probably quite poorly written or idiosyncratic) but there's some interesting metaprogramming going on. Including ActsAsCsv causes the class method self.included to execute with the including class as the parameter. In this case the including class is extended with the ClassMethods module which defines a single method, acts_as_csv, on the class.

Later in the CsvRow class we include ActsAsCsv and then call the acts_as_csv method. This, I guess, occurs at instantiation which means the specific instance of the CsvRow class gains the InstanceMethods module included by acts_as_csv rather than at the class level though in this case it applies to all instances. The InstanceMethods module defines methods which create the behavior of a CSV file. At multiple points the cascade of includes and method definitions is open to tinkering, conditional method inclusion or definition &etc. It's just gotten interesting, and now this 'week' is done. Maybe I'll study Ruby more later.

7L7W Week 1: Ruby Day 2>Take 2

Reading through the 3rd day's treatment and I decided to go back over this exercise from day 2.
This is better. I'm still not entirely convinced.
class Hash
  def each_hash
    h = self
    h.each do |element|
      yield({element[0] => element[1]})
    end
  end
end
class Tree
  attr_accessor :children, :node_name

  def initialize(name, children=[])
    if name.is_a?(String)
      @node_name = name
    elsif name.is_a?(Hash)
      @node_name = name.keys[0]
      name[@node_name].each_hash {|trunk|
        children << Tree.new(trunk)
        }
    end
    @children = children
  end
  
  def visit_all(&block)
    visit &block
    children.each {|c| c.visit_all &block}
  end
  
  def visit(&block)
    block.call self
  end
end

familyTree = Tree.new({'grandpa' => {'dad' => {'child 1' => {}, 'child 2' => {}}, 'uncle' => {'child 3' => {}, 'child 4' => {}}}})
puts "Visiting a node"
familyTree.visit {|node| puts node.node_name}
puts

puts "Visiting entire tree"
familyTree.visit_all {|node| puts node.node_name}


The notion of editing the base class at will and indeed the basic predefined classes/types, while apparently being an integral part of Ruby's design, doesn't come easily. And still this solution requires testing the type of the incoming parameter, making Ruby's blasé treatment of types rather dubious. Or, I'm doing it wrong™ which is also quite possible seeing as this is exactly the 3rd day I've done anything in Ruby.

What I've done here is basically the same as a C# extension method. What gives me the 'icks' is the notion of directly altering Hash.each to do what I wanted it to do. It seems that you can't scope the changes so it's a global edit of the existing class. Great power, &etc, plus TDD, but if anyone comes along later to update the script and tries to do anything with a hash while those changes are in effect it's going to bite them in the tail.

I think I'm feeling a tension between reducing failures and productivity at the cost of stability. It's hard to decide where the sweet spot is there. That's interesting since I spent a good bit of time developing in PHP which is pretty loosely typed.

Tuesday, December 9, 2014

7L7W Week 1: Ruby Day 2>A spoonful of sugar

I feel like I must be missing something. Because this:
class Tree
  attr_accessor :children, :node_name
  def initialize(name, children=[])
    if children != []
      @node_name = name
    elsif name.is_a?(Hash)
      @node_name = name.keys[0]
      name[@node_name].each {|trunk|
        children << Tree.new(trunk)
        }
    elsif name.is_a?(Array)
      @node_name = name[0]
      name[1].each {|trunk|
        children << Tree.new(trunk)
        }
    end
    @children = children
  end
  
  def visit_all(&block)
    visit &block
    children.each {|c| c.visit_all &block}
  end
  
  def visit(&block)
    block.call self
  end
end

looks ridiculous. The main problem seems to be that name[@node_name].each is giving me arrays instead of hashes. Which means I have to account for another type that I can't readily predict in advance... which seems to end up being the bane of dynamically typed languages. Yesyesyes ducktyping, &etc. I can't access elements in a hash the same way I access elements in an array so that's not really applicable here.

On the other hand,
i=0
File.open("data.txt", 'r') do |file|
  file.each do |line|
    i += 1
    puts "#{i} #{line}" if /dialog/ =~ line
  end
end
puts "----------------------------"
puts "Searched #{i} lines of file."

was relatively easy. Except for the false start with File.foreach which didn't seem to actually be giving me full lines from the file. Looked like it was giving me strings the size of some internal read buffer. Frankly, I expect enumerating a file to give me each line. Maybe it was user error.

Still. I'm not particularly impressed with Ruby. It feels a lot like working in PHP 8 years ago. Only with anonymous functions. Day3 is subtitled 'Serious Change' so maybe that'll be interesting.

Monday, December 8, 2014

7L7W Week 1: Ruby Day 1> Hellooooo Ruby

Somehow I was expecting more... wow factor? For as popular as Ruby is and for the kudos it gets it really doesn't, yet, feel more natural or seamless to me. Maybe I've spent too much time with 'harder' or 'crustier' or 'more obtuse' languages to get it. /shrug

Maybe day 2 will have more excitement; I will endeavor to be open minded about it until at least day 5.

Seven Languages in Seven Weeks

Go.

Wednesday, December 3, 2014

Chrome autocomplete

I am amused that I can type 'gith' to go to github.com.
Is Chrome making a statement here? >.>