[e-lang] Couple of newbie questions

Mark S. Miller markm at cs.jhu.edu
Tue Dec 26 23:15:15 CST 2006


Chris Rathman wrote:
> Managed to get through section 2.2.   

Hi Chris, thanks for doing this!

The following probably doesn't stay close enough to the original to suit your 
purpose, but none of your implementations of rational numbers on that page 
seem object-oriented to me. Here's a sketch of how one might actually do 
rational objects in E.


-----------------------------
#!/usr/bin/env rune

# Copyright 2006 Hewlett Packard, under the terms of the MIT X license
# found at http://www.opensource.org/licenses/mit-license.html ...............

# Based on Chris Ratham's makeRational2 at
# 
http://www.codepoetics.com/wiki/index.php?title=Topics:SICP_in_other_languages:E:Chapter_2
# which reduces "to lowest terms in constructor"

pragma.syntax("0.9")

def gcd(a, b) {
     if (b == 0) {
         return a
     } else {
         return gcd(b, a %% b)
     }
}

# internal-only literal guard and stamp
interface Rat guards RatStamp {}

# forward reference
def makeRational

# Exported guard, which knows how to coerce some other things to Rational.
# XXX Rational's FQN is confused, as Kevin Reid explains at
# http://www.eros-os.org/pipermail/e-lang/2006-December/011723.html
def Rational {
     to coerce(specimen, optEjector) {
         if (specimen =~ rat :Rat) { return rat }
         if (specimen =~ i :int)   { return makeRational(i,1) }
         # todo: covert float64 to exact rational
         throw.eject(optEjector,
                     `don't know how to convert $specimen to rational`)
     }
}

bind makeRational {
     # exported guard representing the kind of things this maker makes
     to asType() { return Rational }

     to run(n :int, d :int) {
         def g := gcd(n, d)
         def nn := n // g
         def dd := d // g

         def rational implements RatStamp {
             to numer() { return nn }
             to denom() { return dd }

             /** Implements expansion of "+" */
             to add(other :Rational) {
                 return makeRational(nn*other.denom() + other.numer()*dd,
                                     dd*other.denom())
             }

             /** Implements expansion of "-" */
             to subtract(other :Rational) {
                 return makeRational(nn*other.denom() - other.numer()*dd,
                                     dd*other.denom())
             }

             /** Implements expansion of "*" */
             to multiply(other :Rational) {
                 return makeRational(nn*other.numer(),
                                     dd*other.denom())
             }

             /** Exact divide, which isn't "/" or "//" */
             to divide(other :Rational) {
                 return makeRational(nn*other.denom(),
                                     dd*other.numer())
             }

             /**
              * Implements expansions of <, <=, <=>, >=, >
              */
             to op__cmp(other :Rational) {
                 return (nn*other.denom()).op__cmp(other.numer()*dd)
             }
             to __printOn(tw :TextWriter) {
                 tw.print(`$nn/$dd`)
             }

             /**
              * Enables rationals to coerce to float-accepting and
              * int-accepting numeric types.
              */
             to __conformTo(guard) {
                 if (dd == 1 && nn =~ i :guard) { return i }
                 if (nn/dd =~ f :guard)         { return f }
                 return rational
             }
         }
         return rational
     }
}
--------------------

     ? def one_third := makeRational(1,3)
     # value: 1/3

     ? one_third + one_third
     # value: 2/3

     ? one_third * 2.3
     # problem: don't know how to convert 2.3 to rational

     ? 2.3 * one_third
     # value: 0.7666666666666666

     ? one_third * 2
     # value: 2/3

     ? 2 * one_third
     # value: 0.6666666666666666

     ? def Rational := makeRational.asType()
     # value: <Rational>

     ? (2 :Rational) * one_third
     # value: 2/3

This odd coercion behavior is a necessary consequence of two security issues:

* Rationals are user-defined, whereas ints and float64s are system defined. 
Therefore rationals can know about and rely on ints and float64s, but they do 
not know and cannot rely on rationals.

* In E, infix expressions (except for "==" and "!=") maintain the 
left-to-right trust relationship of normal message sends. The answer is 
according to the receiver (the left operand). In other words, the receiver 
speaks for the result. In "x + y", we can generally rely on the result being 
as number-like as we can rely on x being number-like, irrespective of what 
kind of thing y is. If y is something weird, that's x's problem, not ours.


 > The translation would be a bit
 > cleaner if linked lists were added, but I think the cons, car, cdr, and
 > list functions should get the correct behavior.

Shouldn't the first return in your

     def list(xs) {
        if (xs.size() == 0) {
           return [nil]
        } else if (xs.size() == 1) {
           return cons(xs[0], nil)
        } else {
           return cons(xs[0], list(xs(1)))
        }
     }
be
           return nil
?


Shouldn't the first test in your

     def islist(xs) {
        try {
           if (xs[1] == nil) {
              return true
           } else {
              return islist(cdr(xs))
           }
        } catch err {
           return false
        }
     }
be
           if (xs == nil) {
?

Other than these issues, I don't see how your list-handling code could be 
improved while staying close to the original.


-- 

     Cheers,
     --MarkM


More information about the e-lang mailing list