Wednesday, April 28, 2010

Infinite Types for Fun and Profit

I stumbled upon this fun post at Stack Overflow. Let me paraphrase the goal: write a function contraption that, when given another function (call this the operator) and an argument x, invokes operator(x), and returns a function that, when given yet another argument y, invokes operator(y), and returns a function that, when given yet another argument z, invokes operator(z), and returns a function that ...

That's kind of a gross definition. What we're looking to do, in ocaml, is the following:

let printer = contraption (print_endline)
printer "Hello" "World" "How" "Is" "It" "Going?"

Clearly this should print 6 lines, with the relevant text. Cool, right? You (kind of) get a function that takes an arbitrary number of parameters of the same type (sort of, because you have to do the same thing to each one). So, how do we write contraption? As follows:

let rec contraption op x =
    let _ = op x in
    fun y -> contraption op y

So, what type does contraption have? Well, this is where things get loose. Run this through ocaml and you get:

# let rec contraption op x =
      let _ = op x in
      fun y -> contraption op y;;
Characters 60-76:
      fun y -> contraption op y;;
               ^^^^^^^^^^^^^^^^
Error: This expression has type 'a -> 'b but is here used with type 'b

What? 'a -> 'b isn't the same as 'b? I guess not, but why not let 'a -> 'b equal 'b? Sure, it's an infinite type, but it seems like everything would work out. Indeed, if we run ocaml -rectypes instead we get the following type:

val contraption : ('a -> 'b) -> 'a -> ('a -> 'c as 'c)

What does this type mean? It says that, "if we give it a function from foo to bar, and a bar, then we get back something of an infinite type". And this infinite type is "if you give me a foo, I'll give you back an infinite type", indeed the same infinite type. And that's just what we want!

So, if everything works fine with -rectypes, why isn't it always turned on? Apparently leaving it on all of the time can get you even worse error messages than ocaml already gives you. Hard to believe, I know, but nevertheless there is indeed a deeper circle of Hell.

To bring it back to Mix, we might wonder if this craziness will work in Mix, and how it will look if it does. Here you go:

contraption(op, x)
{
  op(x);
  return function(y)
  {
    return contraption(op, y);
  };
};

Now, how do we use it?

Int main(String args)
{
  var printer = function(s){ return contraption(IO.print, s); };
  printer("Hello")("World")("This")("Is")("Bizarre");

  return 0;
};

Note that since we don't have currying in Mix we were obligated to build an "extra" anonymous function when creating printer; if we had the placeholder syntax "_" of Scala we could have written it as follows:

Int main(String args)
{
  var printer = contraption(IO.print, _);
  printer("This")("is")("Cleaner");

  return 0;
};

Or, if you prefer we can tweak the definition of contraption slightly instead:

contraption(op)
{
  return function(x)
  {
    op(x);
    return contraption(op);
  };
};

Int main(String args)
{
  var printer = contraption(IO.print);
  printer("This")("Is")("Cleaner")("Still!");

  return 0;
};

And so Mix is victorious (or at least works, after a fashion) once again! And incidentally, we see how nice currying can be.
PS - I definitely need to shorten the function keyword; perhaps just fn?

Friday, April 23, 2010

Paranoid

So, I was reading this over on Slashdot this morning, and it got me paranoid. Okay, it didn't really (maybe). But it reminded me that I've wanted an easy way to randomize my MAC address (mostly for fun, so I can pretend that I'm some kind of super-hacker; honestly, no one is interested in tracking my repeated visits to Lambda the Ultimate) for a while.

It should be fairly clear by now that I prefer to write things myself, rather than use an existing tool (as long as I have time, and I can learn something in the process, and it's not too annoying - give me a god damn break, I'm human).

So, I hacked up a MAC address randomizer in Python in a few minutes. It's pretty gross, hard coding stuff everywhere. But I figured somebody might like it for reference (of how not to code, perhaps). Important note: you must run this as an administrator!

import winreg
import random
import os

# This is the name of the interface we want to change.  This can be
# found using "netsh interface show interface", which will list the names
# of all of your interfaces.
INTERFACE_NAME = "Wireless Network Connection"

# This is the registry key holding the item's address.  Unfortunately it has
# a funny name, not based on the INTERFACE_NAME above; we could probably locate
# this from the INTERFACE_NAME somehow, but this was faster.  You will probably
# have several keys of the form '...\0001', '...\0002', etc.  Be sure to
# pick the right one!
KEY_NAME = r"SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0011"
SUBKEY_NAME = "NetworkAddress"
ROOT = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "")

# For some reason Windows 7 requires that the first 2 hexadecimal digits be
# "12".  Also, crappy random number generation.
def generate():
    return ("12%02x%08x" %
      (random.randint(0, 255), random.randint(0, 4294967295)))

# As I couldn't easily find a quick way of enabling and disabling the interface
# using pure Python, so shell it is.
def enable():
    print("Enabling interface...")
    if os.system("netsh interface set interface name=\"%s\" admin=ENABLED"
      % INTERFACE_NAME):
        print("Error: unable to enable interface.")
    return
    
def disable():
    print("Disabling interface...")
    if os.system("netsh interface set interface name=\"%s\" admin=DISABLED"
      % INTERFACE_NAME):
        print("Error: unable to disable interface.")
    return
    
def main():
    try:
        # Here we open up the relevant key for reading and writing values.
        with winreg.OpenKey(ROOT, KEY_NAME, 0,
          winreg.KEY_SET_VALUE | winreg.KEY_QUERY_VALUE) as key:
        
            # No need for this, just for reference; newlines for symmetry.
            (oldMAC, type) = winreg.QueryValueEx(key, SUBKEY_NAME)
            print("Current MAC: %s\n" % oldMAC)
            
            # Make a new key and set it; you can check that it worked using
            # "ipconfig /all" and reading of the adapter's MAC afterwards.
            newMAC = generate()
            winreg.SetValueEx(key, SUBKEY_NAME, 0, winreg.REG_SZ, newMAC)
            winreg.CloseKey(key)
            print ("New MAC: %s\n" % newMAC)
            
            # Reset the interface so it catches our update.
            disable()
            enable()
            print("Done.")
            return 0

    except WindowsError as e:
        # Really bad error handling; if you get 'access denied' you aren't
        # administrator and if you get 'file not found' you probably have the
        # wrong KEY_NAME.
        print("Error: %s" % str(e))
        return -1

exit(main())

Enjoy.

PS - I should confess that all of the comments were added after the fact.

Thursday, April 22, 2010

Again with Immutabililty

So, a while ago I was telling all of my ardent readers that things can get kind of crazy when it comes to the question of using "real methods" for operator+= vs. using syntactic sugar.

Turns out I'm not the only person that's been troubled by this kind of a question; Scala programmers have to worry about it, as do the designers of Scala. Have a look at this post over on Stack Overflow.

Master's Thesis

I've finally sent my Master's thesis, "Realizing the Dependently Typed λ-Calculus", out for review. If that sort of thing gets you sweating, feel free to take a look.