notes-computer-programming-programmingLanguageDesign-programmingConstructs

This page is a disorganized mess of notes. You will probably be more interested in my more organized notes towards a book on programming languages, even though it does not yet incorporate most of the content here.


like everyone else, someday i dream of writing a programming language (even though, right now, i know next to nothing about the subject). i'll call it jasper (notes specifically on Jasper). this page started aspiring to become a list of every (language-supported) programming construct that i've ever heard of, but quickly degenerated into a list of language features, and then into a list of language design notes-to-self of all sorts.

idea from Alan Kay in an interview (http://portal.acm.org/ft_gateway.cfm?id=1039523&type=html&coll=ACM&dl=ACM&CFID=54537199&CFTOKEN=59179419): trying to get x-platform compatibility by writing specs and writing a checker never works. the way to do it is to write the compiler in the new language itself. then it compiles itself to C.

also: " In the \x92\xa1\xc760s, one of the primary goals of the computer science community was to arrive at an extensible language. As far as I know, only three ever actually worked, and the first Smalltalk was one of those three. Another very interesting one was done by Ned Irons, who invented the term syntax-directed compiler and did one of the first ones in the \x92\xa1\xc760s. He did a wonderful extensible language called Imp. "

(he doesn't say what the third one is)

" One of the things that people realized from these extensible languages is that there is the unfortunate difficulty of making the meta-system easy to use. Smalltalk-72 was actually used by children. You\x92\xa1\xc7re always extending the language without realizing it when you are making ordinary classes. The result of this was that you didn\x92\xa1\xc7t have to go into a more esoteric place like a compiler compiler %G\x81— %@Yacc or something like that %G\x81— %@to add some extension to the language."

i don't understand this but maybe its important:

" One of my favorite old languages is one called Lucid by Ed Ashcroft. It was a beautiful idea. He said, \x92\xa1\xc8Hey, look, we can regard a variable as a stream, as some sort of ordered thing of its values and time, and use Christopher Strachey\x92\xa1\xc7s idea that everything is wonderful about tail recursion and Lisp, except what it looks like.\x92\xa1\xc9 When he looked at Lisp, he had a great insight: which was that tail-recursive loops and Lisp are so clean because you\x92\xa1\xc7re generating the right-hand side of all the assignment statements before you do any rebinding. So you\x92\xa1\xc7re automatically forced to use only old values. You cannot rebind, so there are no race conditions on anything.

You just write down all of those things, and then when you do the tail recursion, you rebind all of those variables with these new values. Strachey said, \x92\xa1\xc8I can write that down like a sequential program, as a bunch of simultaneous assignment statements, and a loop that makes it easier to think of.\x92\xa1\xc9 That\x92\xa1\xc7s basically what Lucid did %G\x81— %@there is no reason that you have to think recursively for things that are basically iteration, and you can make these iterations as clean as a functional language if you have a better theory about what values are.

"

http://www.uidesign.net/2000/interviews/cooper1.html

Alan Cooper:

"The point is that, if you are a Visual Designer, or if you are an HCI Professional, or even programmers you tend to approach things from the point of view of saying, what are the technological tools at your disposal. You say, Oh, I have a relational database. Therefore, I can issue a query and I can get back, in a batch mode, a solution set of a reduced number of choices.

You might have a real world situation where you have someone who walks into a library and searches based on a Dewey Decimal Categorization System number and then wants to see a list of related books. The query system fundamentally disallows this."

" Why can't I logically group things? I can categorize things. I can say, here is a name that belongs in my list of business names, here's another which belongs in my list of personal names, but I have lots of names that need to be in both lists. I"

"For example, using any typical operating system e.g. Linux, NT, MacOS?, when you create a chunk of data, you have to put it in a named data block, some file. You then have to put that in some place, in a positionally notated storage hierarchy. So you have to choose a name, and you have to choose and specify a place, a node in a tree. When you want that information back again, you have to remember the name that you gave it and you have to remember the place that you stored it. Then you go to that place, remembering the name, and there it is. You can retrieve that data. It's very logical and it's very appropriate but this is a model which is designed for computers.

The Problem is that humans are really bad at remembering names and places. With that kind of specificity, especially in a recursive hierarchy, where the nodes are exactly alike regardless of what level they are. Of course, computers happen to be really good at remembering stuff like that. However, the computer doesn't give me any help in remembering. The computer delegates that [remembering] job to the human. That's because the guys who invented operating systems.... that sort method comes from Kernighan and Plauger, from Unix. If you're a computer program remembering a simple file name in a directory is a trivial task. They [ the operating system inventors] just handed that task out to the human user.

That is not a simple or trivial task for humans. No one has ever gone back in and said, "Hmmm. Is this appropriate?" I talk a lot about this in my first book, "About Face"."

"If you have a modern cell phone then you know that you have probably recorded into the memory, 50 or 100 phone numbers, and you probably have a speed dial of 8 or 10 numbers which you dial frequently. If you think about the number of times that you key in from scratch, a phone number - area code, prefix, number - is maybe 1 in 10. The other 9 times, you use numbers which the phone has already remembered. Yet the physical interface of the phone which is presented is highly tilted towards dialing those numbers from scratch.

You need a functional overlay and you have to switch into a meta-mode in order to dial up numbers which the phone has already memorized and you regularly dial. This is because we don't think from a Goal-Directed(R) point of view. We don't look at the way people actually do things. Instead we look at the technology and we say, "The way you telephone someone is by typing in a number." Then we say, "Hey we could make this more convenient by having it remember numbers".

Why not instead say, "People always call, the people that they always call". Then you could have something simple like a knob on the top of telephone which just spins through the top 20 people that you call all the time. And by the way, for the rare occasions when you do have to call a strange number, then you turn it over and open up the back and there are the numbers [keys]. "


"detect" control structure:

  1. !/usr/bin/env dpython

A = [9,8,7,6,1,4,3,2,5] i = -1 # Left index j = len(A)-1 # Right index v = A[-1] # Pivot

detect 'Partition done': detect 'Move element j down': i = i+1 if i==j: 'Partition done' if A[i] > v: A[j] = A[i] 'Move element j down' detect 'Move element i up': j = j-1 if j==i: 'Partition done' if A[j] < v: A[i] = A[j] 'Move element i up'

A[j] = v

print A

(from http://www.idi.ntnu.no/~mlh/python/partition.py and "Event-Driven Control Statements", BIT 15, pp. 229-275)


tuples: should be able to return multiple values


vectorization, like octave does, is nice


think about how the web/internet changes things.

a "get" primitive, to get information from some information source (file/ftp/web)?

serialization primitives

distributed computing primitives? remote method invocation? distributed objects?


GUI primitives (seems uneeded)


also, paralled computing primitives (synch/monitor tokens, etc)


text processing primitives, like Perl's RE handling (I don't think Python makes this concise enough)


i think a good way to develop a language is to have a number of test programs, and find the shortest, clearest way to express these programs, then make a language that can understand that

http://merd.sourceforge.net/pixel/language-study/scripting-language/


should have set theory primitives, matrix construction primitives, sequence construction primitives, etc. i.e. the set "union{i\inN} A_i" should be constructible (and lazily evaluated)


generators & conditionals from Icon: http://www.cs.arizona.edu/icon/docs/ipd266.htm


Paul Graham's list of cool things about LISP:

  1. Conditionals. A conditional is an if-then-else construct. We take these for granted now, but Fortran I didn't have them. It had only a conditional goto closely based on the underlying machine instruction.
  2. A function type. In Lisp, functions are a data type just like integers or strings. They have a literal representation, can be stored in variables, can be passed as arguments, and so on.
  3. Recursion. Lisp was the first programming language to support it.
  4. Dynamic typing. In Lisp, all variables are effectively pointers. Values are what have types, not variables, and assigning or binding variables means copying pointers, not what they point to.
  5. Garbage-collection.
  6. Programs composed of expressions. Lisp programs are trees of expressions, each of which returns a value. This is in contrast to Fortran and most succeeding languages, which distinguish between expressions and statements.

It was natural to have this distinction in Fortran I because you could not nest statements. And so while you needed expressions for math to work, there was no point in making anything else return a value, because there could not be anything waiting for it.

This limitation went away with the arrival of block-structured languages, but by then it was too late. The distinction between expressions and statements was entrenched. It spread from Fortran into Algol and then to both their descendants.

  1. A symbol type. Symbols are effectively pointers to strings stored in a hash table. So you can test equality by comparing a pointer, instead of comparing each character.
  2. A notation for code using trees of symbols and constants.
  3. The whole language there all the time. There is no real distinction between read-time, compile-time, and runtime. You can compile or run code while reading, read or run code while compiling, and read or compile code at runtime.

Running code at read-time lets users reprogram Lisp's syntax; running code at compile-time is the basis of macros; compiling at runtime is the basis of Lisp's use as an extension language in programs like Emacs; and reading at runtime enables programs to communicate using s-expressions, an idea recently reinvented as XML.

---

(i'm particularly interested in #9; i sorta want Python, but with #9; thinking about why you can't easily add this on, it seems you need #8 for that to work; but also, i guess you need to design the core of the language with the need to write a compiler for it in itself succinctly)

btw, can you have a program that looks like a graph instead of a tree? might be good for massive parallelism


btw, i think programming languages of the future MUST support massive parallelism; we need to be able to use "active data structures" described in The Connection Machine when computers become capable of it.


might also be interesting to check out http://www.norvig.com/python-lisp.html


http://phaseit.net/claird/comp.lang.python/python_introspection.html:

Cameron Laird's personal notes on introspection in Python In Usenet article <199803021544.KAA17164@eric.CNRI.Reston.Va.US>, Guido followed up

    Good question. Python has a lot of introspection features but some of them are a bit haphazard and others are perhaps less-than-optimally documented.
    (The key ones, of course, are so simple that you don't even think about them -- __dict__, __class__, __bases__, __name__, __file__, as well as locals(), globals(), vars(), dir(), type(), isinstance(), issubclass(), not to mention sys.modules.)
    I've rarely felt the need to extract signatures from functions, but I can see that such a need exists in others.
    Perhaps we can create a list of needed introspection features? 

That's what this page is: "a list of needed introspection features". The initial question had to do with a function which reports a method's signature. Fred L. Drake, Jr., pointed out a number of features beyond those Guido mentions above, including:

        You could try:
        
        	try:
        	    version = func.func_globals['__version__']
        	except KeyError:
        	    version = None
        
          The value of __version__ should be set if the author will make sure
        the module will not be released without updating the number.  One way
        to do this under CVS or RCS would be:
        
        	__version__ = "$Revision$"
        
          Other revision control systems may support similar constructs.

Other possibilities that come to mind quickly (disclaimer: I'm writing this list in creative mode; it's likely that several of them are already implemented, and I just haven't noticed it yet):

exception handling: should allow functions to "throw" exceptions like java, but then, to prevent each higher function from having to throw more and more exceptions, should provide a facility for caller to say, "redefine any exceptions that were not thrown by the immediate child to "ChildException? (where "ChildException?" is something they specify), so that they can be caught all at once (or maybe just, "refine all exceptions thrown by this child to ChildException?), since it's silly to distinguish a black-box subroutine from it's sub-subroutines).

principal: whenever possible, nothing should generate an "error"; rather, an "exception" should be generated, and, there should be a way for someone arbitrarily up the call stack to specify, "if there are any exceptions besides what i've already handled, try to Do The Right Thing and go on, if possible". For instance, it's infuriating how Python crashes if you try to print a Unicode string that can't be converted to your locale. They justify this as, "well, who knows what the right thing to do is? Better crash so that it's obvious that something is wrong". Well, it would be nice to have a sort of global mode saying, "i'm debugging, crash when something is wrong", vs. "print a warning to console when something is wrong but try to continue" vs. "crash when something is wrong". and maybe to have different levels of potential severity (i.e. the damn unicode thing would be a "misdemeanor" and wouldn't cause a crash, whereas failure to open a file might cause one.


not a programming construct, but some notes on other languages from Larry Wall http://wiki.osdc.org.il/index.php/Larry_Wall_-_Present_Continous,_Future_Perfect: http://wiki.osdc.org.il/index.php/Larry_Wall_-_Present_Continous,_Future_Perfect "We've got to start over from scratch" - Well, that's almost any academic language you find.

"English phrases" - We'll that's Cobol. You know, cargo cult English. (laughter)

"Text processing doesn't matter much" - Fortran.

"Simple languages produce simple solutions" - C.

"If I wanted it fast, I'd write it in C" - That's almost a direct quote from the original awk page.

"I thought of a way to do it so it must be right" - That's obviously PHP. (laughter and applause)

"You can build anything with NAND gates" - Any language designed by an electrical engineer. (laughter)

"This is a very high level language, who cares about bits?" - The entire scope of fourth generation languages fell into this... problem.

"Users care about elegance" - A lot of languages from Europe tend to fall into this. You know, Eiffel.

"The specification is good enough" - Ada.

"Abstraction equals usability" - Scheme. Things like that.

"The common kernel should be as small as possible" - Forth.

"Let's make this easy for the computer" - Lisp. (laughter)

"Most programs are designed top-down" - Pascal. (laughter)

"Everything is a vector" - APL.

"Everything is an object" - Smalltalk and its children. (whispered:) Ruby. (laughter)

"Everything is a hypothesis" - Prolog. (laughter)

"Everything is a function" - Haskell. (laughter)

"Programmers should never have been given free will" - Obviously, Python. (laughter)

So my psychological conjecture is that normal people, if they perceive that a computer language is forcing them to learn theory, they won't like it. In other words, hide the fancy stuff. It can be there, just hide it.

   another thing Larry said from that same lecture:Learn as you go Learn it once, use it many times Many acceptable levels of competence Multiple ways to say the same thing No shame in borrowing words Fractal dimensionality Little orthogonality-many shortcuts
       and another (not that i agree with all of these!)Ambiguity is okay if local Use of verbal "punctuation" Use of number, case, and word order Use of pronouns Use of topics Discourse structure, larger contexts
	  and anotherNo theory Style not enforced except by peers Distributed design Inevitable divergence of dialects The usefulness of creole languages Language without culture is dead
	 and anotherAccepting of newcomers (mostly) Subtribes are allowed and encouraged Culture of sharing Culture of capturing knowledge Culture of cooperating with other cultures Most importantly, a culture of fun
     and anotherPerl culture is just as important to the success of Perl as the language itself. In fact, it's probably more important, because the culture can fix the language, but the language cannot fix the culture.

ruby's idea of designating variable scope with glyph prefixes

python and haskell have significant whitespace (optional in haskell)

python has named function arguments, so you don't have to care about order (compare to Haskell, where order often makes things hard to read)

ruby's blocks (should be able to pass in multiple blocks, tho) (i think you can also pass in function objects ("procedure objects") in place of blocks in ruby)

python's generators, with "with"

haskell and python's list comprehensions (python's syntax is better)

auto-return the last expression in a function; "if" can be used to return (in ruby and haskell; although in haskell, functions are just expressions so this is a given). ruby also has a "return" statement

exception handling with blocks and "retry"

ruby syntax for class, superclassing

ruby/haskell has "mixins" of a sort (although not really for haskell)

ruby/python allows you to, by default, have instance variables, which can then later be replaced with getters/setters without changing anything under the hood (attributes in ruby, properties in python)

ruby/python allows you to "tack on" new instance variables or methods to an existing class (open classes). haskell doesn't.

ruby also allows you to tack on new class methods to a single instance (does haskell? almost; you could fairly easily make a new type and make it a member of a new typeclass with the "tack on" method (is this really "fairly easy" or would the syntax be heavier than singleton methods in ruby?)

python and haskell require you to write "import" too much. macros or metaprogramming or a global namespace (so that you could write a module that does nothing but import a list of commonly-used modules into the main namespace) could help?. but watch out for this potential problem, thanks to Brandon J. Van Every at http://groups.google.com/group/comp.lang.python/browse_thread/thread/c41594596595e511/0be4a807ce8a0f66 :

--- Alexander Schmolck wrote:

> Would you still have a problem with macros in python if utilizing them > required some obvious and explicit mechanism (say a > 'use_custom_syntax' statement right at the beginning of a module that > wants to use macros), so that their use could easily be controlled by > e.g. project managers?

Yes you would. In open source communities, you'd get different philosophical camps, and people in one camp would embed 'use_custom_syntax' in some *.h file (yeah yeah I'm a C++ programmer) that all the other *.h files use. When you grab that code, you're not really going to want to do the work of making more specific 'use_custom_syntax' directives, as it would break in all sorts of subtle ways. So now the 'culture of feature' supplants the culture of control. The same could happen in a big commerical project, for that matter. Thus it may be wise not to allow this kind of customization at all. ---

lexical scoping/closures

try/catch/finally

all of them have a C interface. ruby has a python interface.

an 'about ruby' link: http://web.archive.org/web/20050204062537/http://www.ntecs.de/old-hp/s-direktnet/rb/download_ruby.html

Haskell and Ruby distinguish classes with capitalization

python allows object pickling

ruby allows object freezing (i.e. becomes read-only)

perl and ruby have regex syntax (i wonder if haskell could do that..)

everyone but haskell has global variables. mb there is a way to do it in haskell with environment monad?

ruby allows global variables to be traced

ruby has conventions like ! for mutates and ? for boolean. if you have two versions of a fn, an in-place mutating version and a return-the-changed-object version, ! can distinguish them in ruby.

parallel assignmnet: a,b = c,d

in ruby, basic operators can be overridden (i don't like that)

clearly (to me), ruby's reservation of "self" is superior to python's explicit self


basic component programming as a language feature i.e. when you call any function / create any data structure, for instance, a Tree, you should be able to replace it with a balanced tree without much code change. you'd think this would have been done by now, but apparently, even in Haskell, you can just swap out the default string implementation with a more efficient one without rewriting libraries such as a parsing library.

see also Android


towards a "python of haskell"

key for metaprogramming: L's interpreter must be very concise in L. so, if typed language, then type inference must be implemented concisely. mb this is true with logic programming included? if there is parsing, maybe we might as well include parsing stuff as primitives, and maybe also regexps?

typecasting (adhoc?). std library includes useful basic cases like coercion to booleans, for use in conditionals.

concise syntax for typeclass programming for open data (always define typeclasses rather than primitive data types).

concise syntax for strict evaluation, for sequenced evaluation (monads not good enough b/c they change type as well as sequence?), for "syntactic sugar" locally mutable variables (so you don't have to write a, a', a all the time).

good std lib. crib from python (for std methods, i.e. things defined in those std typeclasses), and ruby (for system libs). and of course haskell prelude. also include methods for dealing with trees (lisp s-exps; xml; + the usual binary tree, balanced tree algs; see also c boost) and graphs.

examine python, ruby

uniform error handling (see blog post about 5? ways to handle errors in haskell) error handling should interact nicely with compiler errors in metaprogramming facility

logic prorgamming (prolog) parallel stuff (erlang, lisp*, lots of others) message passing (erlang? and smalltalk) scheme coroutines (see similar in python, ruby (sorta), erlang?, scheme?), iterators what's in self? forth?

eiffel

dont forget to beat basic, pascal, C, C#, bash, lisp (arc), curry, clojure, scala, javascript, chrome, fortran, java, hypercard (hypertalk), (Icon? Io?), lua, rex?, dylan?, perl, php, D, ocaml

special purpose stuff: boomerang, octave, r

mb haskell monads are its way of compensating for lack of metaprogramming; it allows insertion of code between commands

good language for: compilers, text editors, version control, dynamic web sites

see also problems with python

    another prob w/ python: object attributes vs. dicts. why different?

syntax so that quote marks aren't needed so much (i.e. in python, always need quotes to access, say, specific fields in some dict, e.g. HTTP GET request params)

unicode support

"assets" (i.e. graphics, text strings, xml)

std lib with graph, tree structs

versioned library APIs

give up infix functions?!

std, unified exception handling (see http://www.randomhacks.net/articles/2007/03/10/haskell-8-ways-to-report-errors), mb exception specifications (but see http://www.gotw.ca/publications/mill22.htm) but no checked exceptions? (see http://www.mindview.net/Etc/Discussions/CheckedExceptions) (incidentally, to allow checked exceptions, should allow the direct product operation on types --- further ops on types? you want to constrain the args to the direct product to be subsets of types, right (e.g. ("real" types) X (type to keep track of thrown exceptions) )? is this what is meant by "type-level programming"?)

significant whitespace (like python, haskell), but with parens option (like haskell)

logic programming? (see prolog, curry, that planner)

vs lisp: significant whitespace, lazy, punctuation instead of cons, setq

static scoping

vs python: no my, functional, lazy

vs ruby: no ;s, lazy

vs CL, elisp: data and fns share a common namespace

see also erlang, *-lisp

probs w/ delay and force in scheme: "many popular forms of lazy evaluation is actually quite difficult using the Scheme primitives.[5]" -- wikipedia, referrencing http://srfi.schemers.org/srfi-45/srfi-45.html

"Scheme's high level macro system allows the user to add new syntactic constructs to the language. It respects the lexical scoping of the rest of the language, which avoids common programming errors that can occur in the macro systems of other programming languages. Many implementations also provide a more conventional low level macro system."

"Scheme has looping constructs, but it is idiomatic to use tail recursion to express loops. Scheme implementations are required to optimize tail calls to run in constant space."

vs scheme: function-local variable decls

vs haskell: a mechanism to avoid having to define a zillon once-used symbols like x', x, x, by use of statically locally-scoped pseudo-mutable variables

"Besides procedures, continuations, pairs and lists, Scheme provides the following data types: atomic symbols, numbers, booleans, characters, strings, vectors and input/output ports.[1] Association lists are provided by standard procedures, and many Scheme implementations also offer hash tables and such structures.[8] Extensions are standardized through a system of "Scheme Requests for Implementation" (SRFIs).[9]

Since the IEEE Scheme standard and the R4RS Scheme standard, Scheme has asserted that all of the above types are disjoint, that is no value can belong to more than one of these types"

"The numeric type is further divided into a numerical tower, with subtypes complex, real, rational and integer. (Note that these subtypes are not disjoint; in fact each type is a subset of the previous one). While it is not required that a Scheme implementation support the entire numerical tower, most implementations do. In addition to these traditional properties, Scheme numbers may have the property of "exactness". Integers and rational numbers are exact. An arithmetic operation involving numbers one or more of which is inexact has an inexact result.[1]"

"Equality

Scheme has three different types of equality between arbitrary objects denoted by three different relational operators for testing equality, eq?, eqv? and equal?:

Type dependent equivalence operations also exist in Scheme: string=?; compares two strings; char=? compares characters; = compares numbers.[1]"

cond from arc or scheme

language like an API, with versioning and features (somewhat like C compiler constants, and Haskell, and Scheme)

stream processing; stdin, stdout

don't allow run-time redefinition of primitives (avoid this: "The design of Scheme makes it quite hard to perform certain common types of optimisations. For instance, one cannot normally inline calls to primitives (such as + and car) because Scheme allows the re-binding of their names to different functions. ")

source filters?

good read: http://www.python.org/doc/pythonVSscheme.html

some python scheme compilers/interpreters: http://hkn.eecs.berkeley.edu/~dyoo/python/pyscheme/, http://thinkpython.blogspot.com/2005/02/simple-scheme-interpreter.html . ruby: http://github.com/jcoglan/heist . scheme in haskell: http://www.korgwal.com/haskeem/

other less interesting python vs scheme posts: last two comments of http://www.johndcook.com/blog/2009/03/26/mit-replaces-scheme-with-python/

vs python: avoid this:

  1. Gregory Marton wrote:

The trouble with python as the first language people see is that it is inherently confusing.

There are three different kinds of things that “do stuff”, not just functions, but also operators and statements (and lambdas, but that’s not taught at first).

Parentheses are badly confusing to beginners. Consider: >>> max(3,5) 5 >>> print(3,5) (3, 5) >>> print 3,5 3 5 >>> max 3,5 Syntax error: invalid syntax

  1. because print is a statement, whereas max is a builtin function. Huh?

>>> a = () >>> print a () >>> a = (3,5) >>> print a (3,5) >>> a + (7,9) (3,5,7,9) >>> a + (7) TypeError?: can only concatenate tuple (not “int”) to tuple

  1. Because that’s arithmetic parens, not tuple parens >>> a + (7,) (3,5,7)

Is it any wonder people who learn python first come to be afraid of parentheses? Tuesday, March 24, 2009 at 10:27 pm

Permalink

but this is good:

Ryan wrote:

Gregory, no offence but…

“Parentheses are badly confusing to beginners. Consider:”

exhibit a: )))))))))))))

The thing about Python tuples is that simply having a , makes it a tuple. Same with unicode just u’. It takes away much of the verboseness of Java. It also is much more simple than C. Again, allowing the user to focus on the problem at hand making the language a tool not another obstacle. Tuesday, March 24, 2009 at 11:34 pm

Permalink
  the other guy replies (not a very big problem imo):

> The thing about Python tuples is that simply having a , makes it a tuple.

It would be a might easier to teach if that were true, i.e. if this weren’t the case:

>>> a = (,) SyntaxError?: invalid syntax

   and also replies:

> Python has generators, lambdas, closures, and purely run-time types, so despite the enormous syntax difference, it actually has a lot in common with Scheme.

I’m curious, could someone show me how to write (with-timeout 10 (lambda () foo) (lambda () timeout-handler)) using generators? They seem closer to iterators, but I’m a python newbie myself and may not have understood generators well enough.

lambdas are one-liners only, are not very clearly delimited from their surroundings, and if I understand correctly will be getting removed from the language.

closures are problematic:

def make_bisector(initial_low,initial_high): low = initial_low high = initial_high mid = mean(low,high) def bisector(too_low): if too_low: low = mid else: high = mid mid = mean(low,high) return mid return bisector

The = operator is used for both let and set!, so python here makes the decision that the = in the if implies a let at the level of the bisector function, rather than at the level of make_bisector. What one does in python is to use either a hash or an object to store low, mid, and high, but I think it’s hard for python to claim that it has useful closures here.

The enormous syntax difference makes a difference. I’m currently teaching computing to students who have never programmed, and of necessity one must have some language to do it in, and MIT 6.00 uses python too, despite no robots. One of the biggest confusions is about return values vs. side effects (like printing), of course, but another was just between different kinds of things returning, and the order that things happen. I mentioned this before, but the syntax really gets in your way here. Operators take arguments to either side and return a value. Functions are on the left, and they use parens. Statements are on the left and they abhor parens (or rather, treat them as tuple parens, as in my print example). I have watched over and over my students’ eyes light up in understanding when I take the python syntax for a = 3 + max(5,8) and transform it to =(a, +(3, max(5,8))). They are not bothered by the “)))”.

Sorry, meaningful whitespace doesn’t always do well with indentation:

def make_bisector(initial_low,initial_high): ~~low = initial_low ~~high = initial_high ~~mid = mean(low,high) ~~def bisector(too_low): ~~~~if too_low: ~~~~~~low = mid ~~~~else: ~~~~~~high = mid ~~~~mid = mean(low,high) ~~~~return mid ~~return bisector b = make_bisector(0,32) print b(True) print b(False) print b(True)

Gives a syntax error: mid used before it is assigned.

  1. Steven Kryskalla wrote:

Gregory: Python 3.0 introduces the ‘nonlocal’ keyword to solve that problem. See here for more information:

http://www.python.org/dev/peps/pep-3104/

You can also change the definition of bisector to the following to have it work:

def bisector(too_low, mid=mid, low=low, high=high): Wednesday, March 25, 2009 at 8:03 am

Permalink
  1. Schuyler wrote:

Gregory: I don’t think this issue is related to whitespace. Steven’s solution will remove the error, but will not give you what you want. You’re fighting Python’s object-oriented style. I think you’d want something like: http://pastebin.com/m2a457acf

class Bisector: def mean(self,a,b): return (a+b)/2

def __init__(self,initial_low,initial_high): self.low = initial_low self.high = initial_high self.mid = self.mean(self.low,self.high) return

def __call__(self,too_low): if too_low: self.low = self.mid else: self.high = self.mid self.mid = self.mean(self.low,self.high) return self.mid

b = Bisector(0,32) print b(True) print b(False) print b(True) Wednesday, March 25, 2009 at 12:51 pm

Permalink
  1. Adam Olsen wrote:

I second Schuyler’s Bisect class, except for the use of __call__. Not only are you pretending to be a simple function, you’re also returning a result (implying you have no side effect).

As this particular case seems to need the side effect and the result, the next best thing is to find a method name that implies them both. Wednesday, March 25, 2009 at 2:11 pm

Permalink
  1. Trainer Inukuma wrote:

Very good decision! Monday, May 18, 2009 at 8:19 am

Permalink
  1. NedHorvath? wrote:

All the difficulty this thread is having getting the bisector defined suggests that we still have a wee way to go. Who’d have imagined it more than 50 years after the invention of Fortran and Lisp… Monday, May 25, 2009 at 8:59 pm

Permalink

"Despite Python being my bread and butter, I also find it sad that Scheme is no longer the start of everything at MIT (my undergrad program was structured in a way designed to mimic MIT's program, which I found to be very successful intellectually and professionally).

That said, generators (since Python 2.3) are effectively continuations, but if you need "real" continuations, check out Stackless Python, and/or the Greenlets package for regular CPython. There are also anonymous functions in Python (lambda), with some limitations over what some people are used to compared to Scheme (no set!, let, letrec, etc.), but then again, functions/methods with names are really the right thing to use 99% of the time in Python anyways...

While the lack of tail recursion [recursive call elimination optimization] in Python does occasionally bother me, I understand the reasons behind it not having been added years ago: the stacktrace in Python is one of the most important aspects of debugging, and the use of tail call optimization removes stack frames, which would destroy the ability to debug programs using that optimization. Find some way around that limitation, post to python-ideas, and you may be surprised that you get what you want" -- http://muckandbrass.com/web/display/~cemerick/2009/03/24/Why+MIT+now+uses+python+instead+of+scheme+for+its+undergraduate+CS+program?focusedCommentId=4390971#comment-4390971

apparently scheme 5 only has built-in assoc lists (O(N) lookup), not built-in hash tables ("dicts") (O(1) -- or should that be O(log(N)), b/c surely the hash sz should increase w/ # elems? A: http://en.wikipedia.org/wiki/Hash_table#Performance_analysis) also, "alist-delete" is too verbose

python vs scheme: python has better syntax for default function arguments, but scheme evaluates them at runtime and lets you refer to other arguments, which is cool (http://4.flowsnake.org/archives/67)

great essay: http://groups.csail.mit.edu/mac/users/gjs/6.945/readings/robust-systems.pdf he wants provenance "tags", i.e. types, backtracking, constraint propagation (a la python trellis -- used to detect invariant violations, too),

also we should have transactions, STM

many examples of scheme's verboseness in string handling compared to python in this post: http://4.flowsnake.org/archives/29

looks like Python is more verbose here, though scheme has probs too:

http://4.flowsnake.org/archives/21

> (list-ec (: n 1 4) (: i n) (list n i)) ((1 0) (2 0) (2 1) (3 0) (3 1) (3 2))

  1. Python: >>> [(n,i) for n in range(1, 4) for i in range(n)] [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2)]

note also http://srfi.schemers.org/srfi-1/srfi-1.html#iota as scheme's version of range

scheme more verbose for formatted string output: http://4.flowsnake.org/archives/16

scheme looks better for regular fn defns, but worse for keyword and default args (much more verbose, also, the keyword args also appear in the optional positional args list unless you separate them out): http://4.flowsnake.org/archives/12, http://4.flowsnake.org/archives/13

want a language where you don't have to type "" so much in scripts!

see also _why's "potion": http://github.com/fogus/potion/blob/master/README, http://groups.google.com/group/potion-lang/browse_thread/thread/223f6eeb67c85bc4. projects/by people that inspired potion, not mentioned above: http://www.vpri.org/html/work/ifnct.htm, haxe http://haxe.org/doc/intro, lua, rebol. _why is/was a well known ruby guy, so mb his language deserves a look

"Hm, well, the heavy use of DSLs would be one of them… also, Ruby allows for several constructs that are generally considered gratuitous/redundant in the Python world, like “unless”, “x if y”, the use of (arguably) non-intuitive operators rather than descriptive method names, etc. And Ruby likes functional idioms more than Python does (or rather, Guido ;-)."

http://www.windley.com/archives/2008/01/scheme_and_ruby.shtml : " Scheme and Ruby

Duane Johnson pointed me to a very interesting discussion http://news.ycombinator.com/item?id=98227 on Y Combinator about the differences between Scheme and Ruby. This is an excellent discussion—not a flame war—that I found enlightening. The summary, if you don’t want to read the discussion thread:

Can’t say enough about macros. Every language besides Lisp and it’s close relatives trade macros for complex syntax. Maybe that’s a good trade-off, maybe not. Nevertheless, it is a trade-off. You can’t have the full power of macros without simple (abstract) syntax that’s exposed to the programmer.

Now, that I’ve said that, I wonder if IO’s impressive reflection can do the same. Someone more enlightened than I tell me: what can’t be done with reflection that can be done with macros?

I do believe that using reflection is more complex than using macros. It reminds me of poking a stick in a black box to flip a switch.

Posted by windley on January 16, 2008 2:16 PM "

suggestion for arc:

> and I should be able to say just (compare > karma).

I suggest you implement a terse syntax for partial-apply. For my own pet lisps, I use {}. (You are welcome to steal it ;-)

So {+ 1} would expand to (in scheme) something like (lambda rest (apply + `(1 ,@rest)). You could keep partial-applying arguments

  (do
    (= x {+ 1})
    (= y {x 2})

then actually apply it with ()

  (y 3 4 5) ; => 15

Your accumulator generator in Arc:

  (def foo (n) [++ n _])

Could be shortened to:

  (def foo (n) {++ n})   ; even terser than regular Arc!

The only time you'd need _ is when you want to change the order of the arguments (e.g. you'd never need {+ _ 2}, but you might need {/ _ 3}, or even {/ _ _ _ 3} if you want to jam the first three arguments in front.

This has some other interesting properties:

-- http://news.ycombinator.com/item?id=98353

LINQ

i don't agree with most of xahlee's thoughts in these links but...:

 http://xahlee.org/perl-python/what_is_expresiveness.html

http://xahlee.org/Periodic_dosage_dir/t2/oop.html

http://xahlee.org/UnixResource_dir/writ/lisp_problems.html

http://xahlee.org/java-a-day/abstract_class.html

http://en.wikipedia.org/wiki/M-expression mentions I-expressions, which is what i want

http://paulgraham.com/langdes.html i agree with most, except that i think that overloading is important and hence OOP is. if you don't have/use ad-hoc overloading (e.g. python container "protocol", haskell typeclasses), then each data type makes up its own name for analogous functions (e.g. alist-delete in scheme rather than del in python), which clutters code and brains, and prevents code reuse to boot. Compared to haskell typeclasses/java interfaces (which are sort of similar (but kind of opposite, too: see thread http://www.haskell.org/pipermail/beginners/2009-January/000747.html -- see below), and are the simplest ways i know to do ad-hoc overloading), OOP is good for ad-hoc overloading (amongst other things(?)) because inheritance is a natural extension of typeclasses which come with default implementations, and i want default implementations b/c if the programmer just wants some behavior and doesn't need to require a particular implementation, e shouldn't have to (1) hardcode a particular implementation choice into e's program's source code, (2) figure out what the choices are and pick one. todo: email this to pg and see what his solution is, b/c i bet lisp has some alternate way of dealing with this that i don't know about

	  	below: the difference b/t haskell typeclasses and java interfaces is that when you say that a variable holds an object which supports an interface, you are still allowed to call other methods which aren't in that interface on that variable later (if you have other knowledge about the implementation type of the object that lets you assume that it supports other methods), whereas when you say that a variable holds an object which is in some typeclass, you renounce any other knowledge you may have about that variable's implementation type and agree to limit yourself to interacting with it through the interface. the similarity is that in both cases, anyone who the typeclass/interface can call the methods specified in that typeclass/interface on the object, without knowing the specific type implementing the typeclass/interface. you might say that the semantics of typeclasses and interfaces themselves are the same (a set of supported method specifications), but that the semantics of declaring a variable to be of a typeclass type or interface type in haskell or java differ.
		       update: that's wrong, in java you can't call methods outside of the interface! the diff is merely in the convention for how the language tags variables with types; in java, the type can be something that implements the interface, but in haskell the type must be no less specific than the interface itself. so it seems like a meaningless distinction IRL, that is a form of symmetry-breaking (i.e. unnecessary complexity) inherent in type systems.
		       	       here's a better explanation: http://www.haskell.org/pipermail/beginners/2009-January/000758.html. the difference is whether the class definer or the user gets to choose the variable's actual type. i think that post also explains haskell's existensially quantified types.

good explanation of call-by-value, call-by-reference, call-by-name and call-by-need http://theonlywinningmove.blogspot.com/2006/10/scheme-death-knell.html:

"The main ones are call-by-value, call-by-reference, call-by-name and call-by-need. The overwhelming majority of PLs use call-by-value. This means two things - broadly: (1) when you pass a value to a function parameter, the function will not change the value passed. It may perform operations on it using private variables, but it will not actually change the caller's value unless you ask it to. This is distinct from call-by-reference, where variable names all refer, thoughout a program, to the associated memory register. In call-by-refernece, when you name a variable inside a function x, it's the same x as any variables by that name outside the function. Parameters passed to functions are the same as the variables associated with them. Run-time efficient, but computationally confusing. (2) Arguments are evaluated before being passed to the function. This is the disctinction with call-by-name. In call-by-name, arguments are passed as they are to functions. So - to use one of Friedman's examples - the difference can be seen in the return value of this function:

fun x = x + x

Looks like a function that doubles its argument, right? And under call-by-value it is - becuase x gets evaluated before it's passed. But with call-by-need it isn't necessarily. If we said:

x(random 10)

we might not get double anything - might get an odd number. And that's because in call-by-name what gets passed is (random 10) itself. So for each x in the body it is reevaluated. This seems silly, but it's actually very useful in some cases because it prevents code from being unnecessarily evaluated. In addition to sometimes being an efficiency gain (though not always), this has the huge advantage of minimizing chances of going into infinite loops. "

a commenter added http://theonlywinningmove.blogspot.com/2006/10/scheme-death-knell.html#c116188713814571342: "Call-by-need is really just a technique for implementing call-by-name in a purely functional language; arguments to functions are memoized so that they are evaluated only once. Because the language is referentially transparent, this has the same semantics as call-by-name. In a referentially transparent language, (RANDOM 10) would yield the same value without regard for context. So fun x = x + x would ALWAYS double the value, since the argument expression always yields the same value."


disadvantages of laziness:

http://theonlywinningmove.blogspot.com/2006/10/scheme-death-knell.html#c116250719562689747

"there is a problem with not evaluating arguments until the value is needed. Well, two problems. First, because the order in which things are evaluated is not the order things appear in source code, it's hard to imagine a debugger that could possibly be as sweet as debuggers in procedural languages.

More importantly, the unevaluated arguments take up memory. Thus a function like

f x = x + 1

is a ticking time bomb in Haskell. It's only a matter of time until someone calls that sucker in a loop and consumes all your memory with unevaluated thunks that say "((((...) + 1) + 1) + 1)".

I am dead serious. When you run your Haskell program and it goes to 1GB before grinding to a halt, the first thing you need to check is make sure you're not trying to do anything really crazy, like basic arithmetic.

I want to emphasize that I like math, and I like Haskell, and I wish it were all dandelions and body shots. But no one has said anything about this problem that made me feel that using Haskell for a serious project could possibly be classified as sane."

a haskell predecessor: http://www.engin.umd.umich.edu/CIS/course.des/cis400/miranda/miranda.html

http://scienceblogs.com/goodmath/2006/10/haskell_and_scheme_which_one_a.php#comment-248214 : "As a long time Haskell programmer (basically since it came out):

Typechecking is usually very useful, something python could do with. GHC is also blazingly fast. I usually prototype algorithms in Haskell, while GUI type stuff gets done in Python (better GUI support, such as PyObjc?)"

http://alloy.mit.edu/alloy4/tutorial4/

"Daniel: What fails you with GH? For reference, a categorical monad is (definition fetched from Barr: Toposes, Triples and Theories - there it is called a "Triple")

A triple (T,e,m) of and endofunctor T with two natural transformations \xb7:Id -> T, \xbc: T2->T such that \xbc(T\xbc)=(\xbcT)\xbc and \xbc(T\xb7)=\xbc(\xb7T)=Id (normally given by one commuting square and two commuting triangles...)

So, for the correspondences between this and the Haskell Monad concept: The natural transformation \xb7 corresponds to the Monad class function return, \xbc occurs in the use of >>=, but rather more implicitly: >>= takes something already in the monad, say in TA, and a function A->TB which thus ends up inside the monad, and then applies the function to the object, ending up with something in TTB, which with \xbc then gets transformed into something in TB.

Posted by: Mikael Johansson

35
October 29, 2006 5:23 AM

Oh bugger. That's what I get for not rereading properly. Of course I mean the triple to be (T,\xb7,\xbc). " -- http://scienceblogs.com/goodmath/2006/10/haskell_and_scheme_which_one_a.php#comment-250687

"At the heart of computers is copy, compare and branch. Prolog and Sendmail notwithstanding." -- http://scienceblogs.com/goodmath/2006/10/haskell_and_scheme_which_one_a.php#comment-261599

http://scienceblogs.com/goodmath/2006/10/haskell_and_scheme_which_one_a.php#more has some examples in which haskell's syntax is terser:

" Syntax

Syntax is in many ways a minor issue. But for people just learning to program, it can be a big deal. And it's not the case that simpler is always better. I've actually taught an introduction to computer class using Scheme, and the syntax was a big problem.

I think that syntactically, Haskell is a better language for beginners. Haskell syntax is very redundant, which is good for people. Compare these two little snippets:

(define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))

fact n = if n == 0 then 1 else n * fact(n-1)

Which is easier to read? And the Haskell can get even easier to read and write using pattern matching:

fact 0 = 1 fact n = n * fact (n-1)

Try this comparison:

(define (fold init reducer list) (if (eq? list ()) init (reducer (car list) (fold init reducer (cdr list)))))

versus:

fold init reducer [] = init fold init reducer l:ls = reducer l (fold init reducer ls)

The difference gets much more obvious with bigger pieces of code. Between the deliberate redundancies in Haskell's syntax, and the way that pattern matching lets you decompose programs, the Haskell is significantly clearer.

There's another syntax thing that's a lot clearer with Haskell. Both Scheme and Haskell are strongly oriented towards programming in a style where functions are values. When you work that way, one of the most fundamental things you want to do is create new functions. And one of the most common kinds of functions you end up writing is currys: that is, versions of already existing functions with some parameters bound. So, for example, suppose you want to search a list for things with a key of 3. In Scheme, you'd pass a function (lambda (x) (eq? x 3)); in Haskell, you don't need to wrap it into a lambda; you can just write the function with missing parameters. For example, the function to compare to 3 is (3==). Which of those two is easier to read?"


http://programming-musings.org/2007/01/31/a-scheme-bookshelf/ recommends some scheme books:

guildwriter Says: ...Simply Scheme

JJ Says: ...

It s good that you mentioned the paper on HTDP. In my own experience, HTDP works better than SICP. SICP being deeper and more philosophical. Of course, SICP is a computer science classic. Oh, you that have not read SICP you ignorant fools!

--- good post with some metaprogramming links:

http://www.haskell.org/pipermail/haskell-cafe/2008-April/041239.html

he mentions 3-lisp, brown, http://www.metaocaml.org/, and his own language, rosette. "brown" i gather refers to this, which i haven't read: http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=170178 "An algebraic specification of a reflective language".

"behavioral reflection": you can access the code and state of the interpreter which is executing you

here's the scoop on 3-Lisp:

http://www2.parc.com/csl/groups/sda/projects/reflection96/docs/malenfant/ref96/node6.html : " The 3-Lisp level-shifting processor

The stack of meta-interpreters in a reflective tower suggests a direct implementation using interpretive techniques. With a loss of an order of magnitude in performance for each level of meta-interpretation, such a naive implementation is totally useless. Early in the development of behavioral reflection, researchers have proposed ways to avoid the levels of meta-interpretation. In fact, two main efforts have succeeded: an operational approach, with Smith's and des Rivi\xe8res' level-shifting processor (LSP) [dRS84] and a formal approach, with Friedman's and Wand's meta-continuation semantics [WF88].

Both of them apply to the discrete approach and are based on the single-threadedness assumption [DM88]. This assumption says that at any time during the execution, only one of the meta-interpreters needs to be executing, namely the lowest level which runs user's code, either in the program at level 0 (i.e., the meta-interpreter of level 1) or reflective code at some level i in the tower (i.e., the meta-interpreter of level i+1). The single-threadedness assumption relies on the fact that the meta-interpreters above this lowest level need not be executed because they perform an easily predictable computation (the code is known and there is no side-effects).

We now look at 3-Lisp level-shifting processor (LSP), but similar observations can be inferred from the meta-continuation semantics. Using the single-threadedness assumption, a 3-Lisp program p is executed by starting the LSP at level 1 on p, as illustrated in Figure 3.... The LSP at level 1 is executed by a non-reflective version of itself (the ultimate machine) at level 2 (represented by the locus of the solid narrow line). Starting in this configuration is pure speculation. We conjecture that no reflective procedure calls will be made during this run.

When a reflective procedure is invoked at time 2, this conjecture appears to be false and we must conclude that we should have started with at least two levels in the tower running the LSP. This is the case because the body of the reflective must be executed by the level 2. But notice that until this point, the level 2 is the non-reflective version of the LSP, the ultimate machine, which cannot run reflective code. To process the reflective procedure body correctly, the level 2 must be the LSP.

We face two solutions: restart the program from the beginning with a three-level tower with the LSP at level 2 or try to create on the fly the same state of the LSP at level 2 if it had run since the beginning of the program, and to resume the computation from this state. The 3-Lisp LSP adopts the second solution. But, this warm start of level 2 imposes the creation of the values of the level 2 processor registers, namely its current expression, environment and continuation, without having to replay the program. If another reflective procedure is called at time 3, the level 3 LSP must now be created in the expected state we would obtain if it would have been run since time 0, and so on. Notice that when a reflective procedure is run at level n, no code is run at any level below n.

The ingenuity of the 3-Lisp LSP appears in the way it has been written that allows the creation of levels on the fly to be performed."

"In a procedurally reflective programming language, all programs are executed not through the agency of a primitive and inaccessible interpreter, but rather by the explicit running of a program that represents that interpreter. In the corresponding virtual machine, therefore, there are an infinite number of levels at which programs are processed, all simultaneously active. It is therefore a substantial question to show whether, and why, a reflective language is computationally tractable. We answer this question by showing how to produce an efficient implementation of a procedurally reflective language, based on the notion of a level-shifting processor. A series of general techniques, which should be applicable to reflective variants of any standard applicative or imperative programming languages, are illustrated in a complete implementation for a particular reflective LISP dialect called 3-LISP." -- http://portal.acm.org/citation.cfm?id=802050


post that details a terrible mistake in ruby http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg01437.html:

""As for lexical vs. dynamic scope, make no mistake, Ruby is completely lexically scoped. It's just that there's a distinction between methods, which always introduce a new scope, and blocks, which, um, usually do.

To be specific: if the parameter name used in the block exists in the enclosing scope, the existing binding is reused. If not, a new scope is created. This can lead to some wacky behavior: for example, the following works, and prints '6':

fac = lambda{

puts fac.call(3)
xif x == 0 then 1 else fac.call(x - 1) * x end}

However, if you create a binding for 'x' earlier in the same scope, it won't work anymore:

x = 'foo' fac = lambda{

puts fac.call(3)
xif x == 0 then 1 else fac.call(x - 1) * x end}

This outputs '0', because the same binding gets reused for each recursive invocation of the block.

Of course if I turn it around to use "x * fac.call(x - 1)" instead, it'll still work. Such bugs can be hard to catch, but it's easy enough to avoid shadowing names that it doesn't happen much in practice .

HTH, Avi

"

(note on the rest of that post; avoiding typing "lambda" is important too!)


Brackets, Escape, and Run from metaocaml (like quotes, string substitution, and eval):

http://www.cs.rice.edu/~taha/publications/journal/dspg04a.pdf

code generation, like strings, but replaces variables to avoid collisions, and preserves typechecking using a "code" wrapper type, i.e., the type of a bracketed expression is "code [the type of the enclosed expression]"


http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/179642 :

"Ruby is a language designed in the following steps:


if languages with syntax fail to achieve homeoiconicity, mb it's b/c their built-in data structs aren't powerful enough


an interesting post about python's design philosophy (which i find myself disagreeing with more and more, although i do like the end result, so maybe my disagreements are in error) (and two after it which corrects/refines one point that it made) from:

http://www.justskins.com/forums/python-syntax-in-lisp-and-scheme-117446.html

" Alex Martelli Guest Posts: n/a

Re: Python syntax in Lisp and Scheme Posted: 10-04-2003, 05:02 PM Grzegorz Chrupala wrote: ...

            >> >>> def foo(n): 
        >> ... box = [n]
        >> ... def foo(i): box[0]+=i; return box[0]
        >> ... return foo
        >> ... 
    >
    > It's still a hack that shows an area where Python has unnecessary
    > limitations, isn't it? 

Debatable, and debated. See the "Rebinding names in enclosing scopes" section of http://www.python.org/peps/pep-0227.html .

Essentially, Guido prefers classes (and instances thereof) to closures as a way to bundle state and behavior; thus he most emphatically does not want to add _any_ complication at all, when the only benefit would be to have "more than one obvious way to do it".

Guido's generally adamant stance for simplicity has been the key determinant in the evolution of Python. Guido is also on record as promising that the major focus in the next release of Python where he can introduce backwards incompatibilities (i.e. the next major-number-incrementing release, 3.0, perhaps, say, 3 years from now) will be the _elimination_ of many of the "more than one way to do it"s that have accumulated along the years mostly for reasons of keeping backwards compatibility (e.g., lambda, map, reduce, and filter, which Guido mildly regrets ever having accepted into the language).

    > As Paul Graham says (<URL:http://www.paulgraham.com/icad.html>):
    >
        >> Python users might legitimately ask why they can't just write
        >>
        >> def foo(n):
        >> return lambda i: return n += i 

The rule Python currently use to determine whether a variable is local is maximally simple: if the name gets bound (assigned to) in local scope, it's a local variable. Making this rule

Anybody who doesn't value simplicity and uniformity is quite unlikely to be comfortable with Python -- and this should amply answer the question about the motivations for reason number 1 why the above foo is unacceptable in Python (the lambda's body can't rebind name n in an enclosing scope).

Python draws a firm distinction between expressions and statements. Again, the deep motivation behind this key distinction can be found in several points in the Zen of Python, such as "flat is better than nested" (doing away with the expression/statement separation allows and indeed encourages deep nesting) and "sparse is better than dense" (that 'doing away' would encourage expression/statements with a very high density of operations being performed).

This firm distinction should easily explain other reasons why the above foo is unacceptable in Python: n+=i is a statement (not an expression) and therefore it cannot be held by a 'return' keyword; 'return' is a statement and therefore cannot be in the body of a 'lambda' keyword.

        >> or even
        >>
        >> def foo(n):
        >> lambda i: n += i 

And this touches on yet another point of the Zen of Python: explicit is better than implicit. Having a function implicitly return the last expression it computes would violate this point (and is in fact somewhat error-prone, in my experience, in the several languages that adopt this rule).

Somebody who is unhappy with this drive for explicitness, simplicity, uniformity, and so on, cannot be happy with Python. If he wants a very similar language from most points of view, BUT with very different philosophies, he might well be quite happy with Ruby. Ruby does away with any expression/statement distinction; it makes the 'return' optional, as a method returns the last thing it computes; it revels in "more than one way to do it", clever and cool hacks, not perhaps to the extent of Perl, but close enough.

In Ruby, the spaces of methods and data are separate (i.e., most everything is "an object" -- but, differently from Python, methods are not objects in Ruby), and I do not think, therefore, that you can write a method that builds and returns another method, and bind the latter to a name -- but you can return an object with a .call method, a la:

def outer(a) proc do

ba+=b end end

x = outer(23) puts x.call(100) # emits 123 puts x.call(100) # emits 223

[i.e., I can't think of any way you could just use x(100) at the end of such a snippet in Ruby -- perhaps somebody more expert of Ruby than I am can confirm or correct...?] but apart from this it seems closer to what the above quotes appear to be probing for. In particular, it lets you be MUCH, MUCH denser, if that is your purpose in life, easily squeezing that outer function into a (short) line. Python is NOT about making code very dense, indeed, as above mentioned, it sees _sparseness_ as a plus; a typical Pythonista would cringe at the density of that 'outer' and by contrast REVEL at the "sparsity" and "explicitness" (due to the many names involved:-) of, e.g.:

def make_accumulator(initial_value): accumulator = Bunch(value=initial_value) def accumulate(addend): accumulator.value += addend return accumulator.value return accumulate

accumulate = make_accumulator(23) print accumulate(100) # emits 123 print accumulate(100) # emits 223

(using the popular Bunch class commonly defined as: class Bunch(object): def __init__(self, kwds): self.__dict__.update(kwds) ). There is, of course, a cultural gulf between this verbose 6-liner [using an auxiliary class strictly for reasons of better readability...!] and the terse Ruby 1-liner above, and no doubt most practitioners of both languages would in practice choose intermediate levels, such as un-densifying the Ruby function into:

def outer(a) proc do

a+b end end
b

or shortening/densifying the Python one into:

def make_accumulator(a): value = [a] def accumulate(b): value[0] += b return value[0] return accumulate

but I think the "purer" (more extreme) versions are interesting "tipizations" for the languages, anyway.

Alex

Reply With Quote Alex Martelli ts Guest Posts: n/a

Re: Python syntax in Lisp and Scheme Posted: 10-04-2003, 05:22 PM >>>>> "A" == Alex Martelli <aleax@aleax.it> writes:

A> [i.e., I can't think of any way you could just use x(100) A> at the end of such a snippet in Ruby -- perhaps somebody A> more expert of Ruby than I am can confirm or correct...?]

Module#define_method

Guy Decoux

Reply With Quote ts Christoph Guest Posts: n/a

Re: Python syntax in Lisp and Scheme Posted: 10-04-2003, 10:53 PM "Alex Martelli" wrote: ....

    > def outer(a) proc do |b| a+=b end end
    >
    > x = outer(23)
    > puts x.call(100) # emits 123
    > puts x.call(100) # emits 223
    >
    > [i.e., I can't think of any way you could just use x(100)
    > at the end of such a snippet in Ruby -- perhaps somebody
    > more expert of Ruby than I am can confirm or correct...?] 

Guy is probably thinking about something like this

--- def outer(sym,a) Object.instance_eval { private # define a private method define_method(sym) {

} end
ba+=b }

outer(:x,24)

p x(100) # 124 p x(100) # 224 ---

but there is no way to write a ``method returning method ::outer in Ruby that could be used in the form


x = outer(24) x(100)


On the other hand, using []-calling convention and your original definition, you get - at least visually - fairly close.

--- def outer(a) proc do

ba+=b end end

x = outer(23) puts x[100] # emits 123 puts x[100] # emits 223 ---

/Christoph "


languages that are haskell expressed as s-exprs are "liskell" and "hasp". diff is that hasp is a preprocessor, liskell is a GHC extension. hasp cannot do true gensyms b/c it can't interface with the compiler (it uses hopefully unique prefixes for its symbols, things like "s_gensym". liskell has true gensyms.


" > I agree with everything you say, but suspect that writing > good code that is easy to understand, maintain and extend > may only be achieved by professional discipline. > > If anyone knows of a language that can help us in this > respect, I would *love* to hear about it...?

Ruby (in my opinion) does well in this regard (even though it comes with even more aparatus that CAN be used for foot-shooting than C++), as does Eiffel."


http://www.artima.com/forums/flat.jsp?forum=106&thread=179766&start=15&msRange=15 " Posted: Oct 23, 2006 10:58 AM Reply to this message Reply Just in case this might interest someone. Implicit parameters, conversions, overloading, inheritance, etc... aren't the only non-intrusive ways to extend libraries a posteriori. Standard ML (http://en.wikipedia.org/wiki/Standard_ML), for example, has a nice module system, lexical scoping, and immutable bindings that make it possible to extend libraries. This can be even done without compromising existing code that uses the (original non-extended) library (although it is easier to do it with language extensions specifically designed for that like the ML Basis System: http://mlton.org/MLBasis). Here is a small project of mine that extends the Standard ML Basis library (http://www.standardml.org/Basis/) without actually modifying the source code of said library:

http://mlton.org/cgi-bin/viewsvn.cgi/mltonlib/trunk/com/ssh/extended-basis/unstable/

Martin Odersky

Posts: 73 Nickname: modersky Registered: Sep, 2003

	Re: Pimp my Library 	Posted: Oct 23, 2006 5:11 PM 	Reply to this message 	Reply I am puzzled because Scala's object system subsumes SML's module system (more precisly, the generative facet of it). Yet implicits are in my mind quite orthogonal to this, much more akin to Haskell's type classes. What aspect of the module system did you have in mind as an alternative to implicits?

Vesa Karvonen

Posts: 116 Nickname: vkarvone Registered: Jun, 2004

	Re: Pimp my Library 	Posted: Oct 24, 2006 4:42 AM 	Reply to this message 	Reply Sorry, I guess I wasn't clear enough. I wasn't suggesting modules as a replacement to implicits or type classes. I was responding mainly to this statement in your original post:

"There's a fundamental difference between your own code and libraries of other people: You can change or extend your own code, but if you want to use some other libraries you have to take them as they are."

What I'm saying is that SML's features (particularly when combined with something like SML/NJ's CM or MLton's/MLKit's ML Basis System) make it possible to safely extend libraries made by others. The "Extended Basis Library" does just that (it also uses the ML Basis System, but the only thing that can not be really done within SML'97 is the hiding of some auxiliary module definitions (particularly functors)).

The way I see it, SML is a simple language. Complex or unsafe language features aren't needed to extend libraries.

 Martin Odersky

Posts: 73 Nickname: modersky Registered: Sep, 2003

	Re: Pimp my Library 	Posted: Oct 24, 2006 5:58 AM 	Reply to this message 	Reply > Sorry, I guess I wasn't clear enough. I wasn't suggesting > modules as a replacement to implicits or type classes. I > was responding mainly to this statement in your original > post: > > "There's a fundamental difference between your own code > and libraries of other people: You can change or extend > your own code, but if you want to use some other libraries > you have to take them as they are." > > What I'm saying is that SML's features (particularly when > combined with something like SML/NJ's CM or > MLton's/MLKit's ML Basis System) make it possible to > safely extend libraries made by others. The "Extended > Basis Library" does just that (it also uses the ML Basis > System, but the only thing that can not be really done > within SML'97 is the hiding of some auxiliary module > definitions (particularly functors)). > My statement was too strong. Sure, a decent module system like ML's will allow you to extend modules with new functionality (the same goes for a standard object system). But there are things you can't do. For instance if your base library produces values of type X you can't make it retroactively produce values of some tupe X' which is richer than X, without changing its code (unless the library has forseen the extension using factory methods, but that's another story).

> The way I see it, SML is a simple language. Complex or > unsafe language features aren't needed to extend libraries.

Well, I think implicits are neither complex nor unsafe. Bob Harper, one of the designers of the SML module system, seems to agree. He proposes to change the module system in order to allow it to simulate Haskell's type classes. The proposal is very close to Scala's implicits.

 Vesa Karvonen

Posts: 116 Nickname: vkarvone Registered: Jun, 2004

	Re: Pimp my Library 	Posted: Oct 24, 2006 7:32 AM 	Reply to this message 	Reply > But there are things you can't do. For instance if your base library > produces values of type X you can't make it retroactively produce values > of some tupe X' which is richer than X, without changing its code (unless > the library has forseen the extension using factory methods, but that's > another story).

Depends on what you mean. In SML, assuming you a have module whose signature defines an abstract type X, you can indeed rebind the module with an augmented module that has a richer definition of type X. If you have multiple modules sharing the same abstract types, you can rebind all of them. Of course, such rebinding does not change the meaning of code that refers to the original module.

> > The way I see it, SML is a simple language. Complex or > > unsafe language features aren't needed to extend libraries.

Perhaps my wording was a bit too strong here.

> Well, I think implicits are neither complex nor unsafe.

Admittedly I don't know Scala (aside from skimming a few papers on it), but I think that combining implicit conversions and mutation is a great way to shoot yourself in the foot. Time will tell.

> Bob Harper, one of the designers of the SML module system, seems to > agree. He proposes to change the module system in order to allow it to > simulate Haskell's type classes. The proposal is very close to Scala's > implicits.

I'm aware of the work. I'm personally not too enthusiastic about it. I think that there are far more important extensions (e.g. higher-order functors, higher-rank polymorphism, polymorphic recursion, ...) that should be considered/added to SML before anything like implicits or type classes that add an additional level of magic to the language. It is a good thing that the behaviour of SML programs can be explained without preprocessing or translating the program to some elaborated from (well, almost). Adding type classes destroys that property (you then need something like the dictionary translation).

Martin Odersky

Posts: 73 Nickname: modersky Registered: Sep, 2003

	Re: Pimp my Library 	Posted: Oct 24, 2006 1:10 PM 	Reply to this message 	Reply > > But there are things you can't do. For instance if your > base library > > produces values of type X you can't make it > retroactively produce values > > of some tupe X' which is richer than X, without changing > its code (unless > > the library has forseen the extension using factory > methods, but that's > > another story). > > Depends on what you mean. In SML, assuming you a have > module whose signature defines an abstract type X, you can > indeed rebind the module with an augmented module that has > a richer definition of type X. If you have multiple > modules sharing the same abstract types, you can rebind > all of them. Of course, such rebinding does not change > the meaning of code that refers to the original module. > True (and the same holds for abstract type members of classes in Scala). But an abstract type is just the type-level analogue of a factory method -- both enable planned extensibility. The SML base library is very well designed in that respect, that's why it is so extensible.

Many other libraries are designed with less forethought, so you have to use them as they are. My post was about what to do for them. I absolutely agree that implicits are not a panacea; when extensions are planned beforehand, type/method abstraction is usually preferable.

Jay Sachs

Posts: 30 Nickname: jaysachs Registered: Jul, 2005

	Re: Pimp my Library 	Posted: Oct 26, 2006 10:52 AM 	Reply to this message 	Reply > I am puzzled because Scala's object system subsumes SML's > module system (more precisly, the generative facet of it). > Yet implicits are in my mind quite orthogonal to this, > much more akin to Haskell's type classes. What aspect of > the module system did you have in mind as an alternative > to implicits?

I may be wrong, but I thought Haskell does not have ad-hoc overloading, while Scala does. In particular, I'm under the impression that Haskell doesn't have to "find the best fit among potentially ambiguous functions" that Scala does.

Or does the same danger exist in Haskell? (Forgive any and all abuses of Haskell that follow).For example, if I have a type T which "implements" a class C, but does not "implement" D, is it possible for me to "import" something which will "adapt" C to D, and thus T will "implement" D? And by doing so, this could change which function "someFun" in

someFun t

is actually invoked? Does Haskell do anything with potential name collisions besides spit out an ambiguity error?

All this is not to say that "just because Haskell does it, it must be good to do". There is much to emulate in Haskell, but it's not all gold.

Martin Odersky

Posts: 73 Nickname: modersky Registered: Sep, 2003

	Re: Pimp my Library 	Posted: Oct 28, 2006 4:34 AM 	Reply to this message 	Reply > I may be wrong, but I thought Haskell does not have ad-hoc > overloading, while Scala does. In particular, I'm under > the impression that Haskell doesn't have to "find the best > fit among potentially ambiguous functions" that Scala > does. > True. Haskell has other magical things, though, such as default cases for instances.

> Or does the same danger exist in Haskell? (Forgive any and > all abuses of Haskell that follow).For example, if I have > a type T which "implements" a class C, but does not > "implement" D, is it possible for me to "import" something > which will "adapt" C to D, and thus T will "implement" D? > And by doing so, this could change which function > "someFun" in > > someFun t > > is actually invoked?

Yep, you can certainly do that in Haskell. Even without the import. An instance definition *somewhere* in your program will suffice. (I don't like this behavior; that's why Scala has the visibility rule for applicable implicits).

> Does Haskell do anything with > potential name collisions besides spit out an ambiguity > error? > Here I am not sure.

> All this is not to say that "just because Haskell does it, > it must be good to do". There is much to emulate in > Haskell, but it's not all gold.

Right, but what would Haskell be without type classes?

Cheers

-- Martin

Andy Dent

Posts: 139 Nickname: andydent Registered: Nov, 2005

	Re: This was a bad idea in C++ 	Posted: Oct 28, 2006 8:58 PM 	Reply to this message 	Reply > C++ allows things that are not only problematic from a > maintenance perspective, it also allow things that are > dangerous and, frankly, unnecessary.

I have long thought that there should be a library switch in c++ and this should be enforceable at a level where an engineering manager can dictate it is off for their application development ;-)

The much-denigrated conversion and operator-overloading features in C++ are fantastic when you are writing core libraries and adding new types, creating a DSL (Domain-Specific Language).

"


linq

some python scheme compilers/interpreters: http://hkn.eecs.berkeley.edu/~dyoo/python/pyscheme/, http://thinkpython.blogspot.com/2005/02/simple-scheme-interpreter.html


http://www.infoq.com/interviews/F-Sharp-Don-Syme

"

There was one project called O'Haskell, which was interesting, but there was something about Camel which had a tradition of taking a call language and then making extensions to it, changing it, experimenting it, but taking a core paradigm that works very well and then making it fit to a context. In a sense, in Haskell there are too many points where the model wasn't going to work in the context of .NET and you get too much dissonance across the interoperability boundary in particular - actually there are some serious challenges in making it run well at all. That was definitely an interesting project and I think a few other people have tried the Haskell .NET, but we didn't continue with the project.

...

The promise of F# was this seamless interoperability with the .NET framework. Did you achieve that? Don't you think that the problem got transferred to semantic differences between 2 paradigms? It depends what you mean by "seamless" and sure, that would be a word I'd use for any kind of interoperability. Certainly, when we use C# to F# components, F# to Visual Basic components and Visual Basic to C# it's amazingly easy to take components written in another language and use them inside a .NET application. Actually, I would say it's fairly seamless - I mean the syntax is different between the languages at the interoperability boundary of the .NET, then the F# concurrency and just about any C# API endured in a natural way.

We put a lot of work into that level to make sure that using .NET APIs is as seamless as it can be. The second half of your question is about semantics - at a fundamental level, the semantic model of F# is similar to the semantic model of C#. In fact, the problem with semantic models isn't that they are so much different, but it's more that you want, in functional programming, to have less - you take things away. By taking things away you get this "less is more" effect: you get programs which have less, in the sense that they are more pure, but, by getting that purity you get some properties out of your program.

You get to refactor your programs or you get to do equational reasoning between your programs, so you get to transform them in certain ways or you get these lovely properties where you can abandon computations and you know that they haven't had any side effects and the like.

Since the fundamental model we execute is IO code and you can do imperative programming in F#, but the approach we take is not to say we are semantically fundamentally different language, but the language is constructed in such a way to orient you towards doing functional programming and to using less state, it's a transition point, in a way, towards more pure programming.

....

The purpose of the object model is to allow people to write certain kinds of libraries. In order to know how use the libraries you end up having to implement a full object model. Certainly, you can implement the full object model and then you have to be able to declare the full object model in the languages - that's just inevitable. There are some constructs we added a little bit reluctantly and some which we gave a very different and productive interpretation in F# - for example events, in C#. We didn't just take the notion of an event from C#; we instead created this notion of "first class events" in F#.

....

We can actually apply functional programming when you get an event stream, so a time you think of it as a functional reactive programming with stream events coming in and you want to filter that and you want to map it and transform that and folder cross it so scan it and collect some state and then meet new events. You can do that in this very nice compositional functional programming style based on these events coming in as first class objects, so you can take a form, get the click event and you can map it and filter it and the like and then you meet a composite event at the end.

There is this case when importing something it's actually like C# had a very deep insight it's like the imperative version of functional reactive programming and it's interesting to look at the parallels between the 2 events and behaviors. They put into the language a construct, which was unusual - it was new, but very fundamental in making C# a good reactive programming language in the sense of reactive and responding to streams of events. We took that C# insight and gave it a functional language interpretation.

Often, laziness gets associated with functional programming and we know that F# is, somehow, a functional programming language, yet you chose to make it strict by default and laziness as option. Why did you decide to do this? It's an obvious decision to make in the context of .NET. Laziness brings a lot of challenges in a programming language. If you make it truly everything lazy, then you have all sorts of efficiency problems, all sorts of debugging problems, of interoperability problems. Often, traditionally people have designed their own execution infrastructure, so even getting code to compile to a different execution infrastructure like the .NET common language runtime can be quite challenging.

The question would really be what would you gain from laziness and I think that's well understood in the context of Haskell. Some programs do end up nicer when everything is lazy, but you pay a cost for that as well, which is suddenly everything is a computation. You have a function and it's getting an argument and says it's an integer but you might poke on that integer and it might never turn, so there is a cost. Everything is implicitly a computation. It's an interesting design choice and a very powerful one in the context of Haskell.

....

The thing you really gain in Haskell is you create this network of recursive objects, which is a problem that has interested me as a researcher in the context of strict languages. F# does have mechanisms - think of strict recursive objects that create a network of objects from the initialization graph and executes the initialization lazily, as a graph. You can't be sure that this process won't evaluate things before they are ready.

...

To answer your question: it would be unusual to make a language on .NET lazy; it would lead you to very many challenges.

...

Talking about Haskell, what do you think of type classes? We were having a chat before we started about design elements, like what process we use for deciding what would go into F# and what wouldn't go into F#. Type classes play a very interesting role in Haskell: they began life as a way of doing overloading, a way of making sure plus could work over different types that you want to work with. Then, they found it's actually a very useful way of propagating information through the program.

It's a lot of things you can do with type classes, which are very interesting, but it's also an area which it's not clear where the stable points are in the design path. You get these discussions about, like functional dependencies in Haskell, or overlapping instances or these diamond properties where 2 different modules declare different incompatible instances of the same type class and they conflict when they are both imported. There is quite a surprising number of technical problems associated with type classes.

We have once a clearly powerful mechanism, but there is this slippery slope from what it's originally useful, which is an important problem, which is overloading to very advanced functional programming, which is still not sure if will ultimately lead to a higher productivity in the end or not and lots of technical detail to look after. In the approach we've taken to F# we actually use the type class mechanism for a very weak version of type classes for overloading.

When you have a plus in F#, you will use constrained type inference, which is called from type classes to propagate these constraints around and eventually generate a witness for "OK, we've got an integer on the left, and there is an integer on the right - we must be doing integer to finally work out the way, we really do integer addition." In that sense, we use a mechanism very similar to type classes and it's a version of constrained type inference for the original problem the type classes was looking after with overloading.

It's been very much on my mind, as one potential evolution step for the F# language is to do later like this - the theme of type classes, unless you write these generic components, which are generic everything, like arithmetic types, and over other kind of sets of parameters: monads and so on. Those components have real uses. There are other ways of writing those components in F#; we can still write those components, you just pass the witnesses, the dictionaries explicitly and that works fairly well. In fact, there is a way to make it explicit because we've got interoperability with C#. If C# wants to use one of these generic components that we write, they can actually instantiate it - they have to do it explicitly, of course. Type classes love the mechanisms fantastic and it's a matter of choosing elements along the design access, which are appropriate in the context of .NET and in the context of what F# is trying to achieve.

There is another problem that happens with the type classes, which a lot of languages have: you end up encoding quite a lot of the logic of your application in the type class system. It's actually like the type checkers are effectively running this Prologue interpreter halfway through your type checking and half your program ends up running a type checking time, which is great. I mean that's interesting, but in F# we try to make things simple and clear, so a simpler foundation anyway, and so we don't look for that mechanism quite so much as driven by ease in the Haskell world.

....

Where does the power of F# come from? What is the power of F#? For me, F# is a fantastically productive language for doing .NET programming and I characterize it as data oriented programming and sometimes control oriented programming. So you are analyzing large log files on the data orientation side or you might be doing some parallel programming on the control side. I always like to think that some of the power comes from the language and quite a lot of the power comes from the platform. F# gets a lot of it's power from the richness of the .NET platform, but .NET actually gets a lot of simplification and power from the additional of a functional language to the platform. Do you think that F#, because now it's getting a first class language in the .NET platform, competes with C# or each one will have its own domain of application? We see the 2 as very synergistic: the F# is done in the same team as C# and, in fact, some of the same people and the designer of C#, Anders, has just recently given a talk on the future of programming languages and gave demos of F# halfway through his talk and I think he really clearly sees in his mind that C# is a wonderful mature component oriented programming language with an amazing set of tools inside Visual Studio and its Visual Studio incarnation - the monad implementations and the like, as well. F# just doesn't rival that language in the sense of the domains where C# is absolutely excelling and object oriented programming and our component oriented programming and tool oriented programming.

That said, we love investing in new languages and we see it as very important for the platform. It's not really about language, it says more about domain. There are domains where F# really does excel at, these sort of data processing domains and the like. It's those domains that you see people turning maybe away from the object oriented languages and turning to Python on every reason, to F# because Ruby and the like have different strings as well.

I guess the F# does pick up some of its support in the user base from those problem domains where people still need to use a typed language, but it's just not the greatest fit for object oriented programming. That really doesn't place them in competition - they are very complementary to each other. As a matter of fact, they integrate and inter-operate so well, really does mean that you can actually choose to tune your point, like if you are writing in mixed F# - C# application.

It's quite common: you do the presentation stuff in C# and you do some core algorithms in F# or core data processing and you can choose the tuning point - it's not so much about language, it's more based on the culture of your company or the expertise you have available or the legacy code basis that you might have. It's not a hard cut line that you have to do this here, there is a great patch in the middle. Frameworks, like Ruby on Rails, get a lot of success and there is an argument that says that the success came from the fact that it's implemented using the semantics of Rails. Do you think that will or should be frameworks written with the semantics of F# for persistence or web or whatever other concerns? We certainly laid foundations, which enables those frameworks. In the .NET world, people always have to face this interesting choice about "Do we do a generic multilanguage component that is going to work on .NET IL, for example, or do we do a component that is specific to a particular language?" I will expect to see a lot of existing components, say for instance object relational mapping kind of components - we'll see those working to target both C# and F# and Visual Basic and all the .NET languages. Maybe they'll do that, but it's generating C# code - it's quite common. We definitely laid the foundations in the language for very interesting language specific frameworks.

We've had people at Microsoft Research for example do web programming frameworks where you could write both the client and the service side and you could mark up the parts of your program as being client or server and the client side was run as JavaScript? and the service side was run as native code through the common language runtime. This is really a very interesting project because it showed how far you could get in creating this kind of framework and getting type checking across this boundary and it's quite a heterogeneous system you re executing.

In fact, you could have parts of your service side program being database queries, which are actually run on the database, so you are going to get this 3D kind of application clients over database, in a very nice way. We'll see what people make of it. Our job is to provide the foundations and people are going to say all sorts of things. Just recently, there is a company called Avanade, running fragments of F# programs, a functional programming subset on FPGA circuits and that's fantastic. We've set the foundations to run the F# code in all sorts of interesting ways: on the GPU, on the FPGA, on databases. Usually, I don't know why, but each time we talk about F#, there is Scala that is mentioned, as if there are some similarities between them. What do you think of Scala? First of all, I'm incredibly thankful to the Scala research group because I got to spend a month there, 5 weeks, last year as part of an extended visit and I learnt a lot that had a lot of influence on the design of F#, so I'm very grateful to Martin Odersky for that and for the advices given about F# along the way. I think people make the comparison because they embraced in some sense both functional and object oriented programming and because they are both seen as sort of next step languages. People make this C# - Java comparison and people make the next typed language comparison.

The emphasis is surprisingly different: Scala is very much about better component oriented programming for the Java platform. Although we do a good job of object oriented programming which is very nice in F#, we haven't thought to make fundamental improvements at the component level, in a sense. We are quite happy to say "You are making components? OK, make it a .NET component".

....

There is an interesting feature of F#, called asynchronous workflows. Can you tell us briefly about this feature? They are basically a way of writing lightweight agents in F# code. Basically doing user level non-blocking flows of control. You can mark up these points where you want to do an asynchronous read from the web and when that read comes back from the other side of the planet - when the response actually comes back - then you continue the program from this point. Or maybe you want to do several asynchronous requests in parallel and then bring them back. We can do all sorts of user threading work, easy in this mechanism and that's fantastic. It's actually built on the foundations of monads most famously from Haskell.

Monads were originally about taming side effects and then people discovered they are actually very good for doing monads for data as well. A mechanism that was originally used for side effects in Haskell ends up being used for queries in C#, which is about data and ends up being used for control in F#, which is asynchronous workflows in parallel and user level agents and the like. "

http://neilmitchell.blogspot.com/2008/12/f-from-haskell-perspective.html


printf in haskell:

http://hackage.haskell.org/packages/archive/liboleg/2009.9.1/doc/html/Text-TotalPrintF.html http://en.wikipedia.org/wiki/Cayenne_%28programming_language%29 https://hackage.haskell.org/package/base-4.8.2.0/docs/Text-Printf.html


random haskell pages

http://users.aber.ac.uk/afc/stricthaskell.html

http://cvs.haskell.org/Hugs/pages/users_guide/quantified-types.html

http://en.wikipedia.org/wiki/Generic_programming#Generic_programming_in_Haskell http://www.generic-haskell.org/ http://www-ps.informatik.uni-kiel.de/~sebf/projects/traversal.html http://www.haskell.org/haskellwiki/?title=Scrap_your_boilerplate&redirect=no http://www.haskell.org/haskellwiki/Uniplate


"Haskell is one of the languages that can completely change the way you think about programming along with languages such as Lisp and Scheme, Forth, Joy, J, Smalltalk, Self, Erlang and Prolog." -- http://www.oreillynet.com/mac/blog/2006/03/haskell_vs_ocamlwhich_do_you_p.html#comment-34114

---

should check out perl6

http://en.wikipedia.org/wiki/Qi_%28programming_language%29 http://en.wikipedia.org/wiki/Clean_%28programming_language%29 http://en.wikipedia.org/wiki/Mercury_%28programming_language%29


what's up w/ javabeans?

should check out Zope more

pipes, filters? is a stream any different from a generator or iterator? what's the diff b/g gen and it? see http://en.wikipedia.org/wiki/Flow-based_programming

http://en.wikipedia.org/wiki/Dataflow , http://en.wikipedia.org/wiki/Reactive_programming

http://en.wikipedia.org/wiki/Concatenative_programming_language

dylan alloy


are scheme macros the same as haskell compiler's "graph transformations"?

can haskell's lazy pattern matching always be replaced by a few "head"s?

http://deposit.ddb.de/cgi-bin/dokserv?idn=984097260&dok_var=d1&dok_ext=pdf&filename=984097260.pdf

Structured Generic Programming in Eden

dataflow (gamma formalism): http://lambda-the-ultimate.org/node/1632

ocaml uses indentation and uses ;; to remove some sort of ambiguity (which i don't understand)

http://www.podval.org/~sds/ocaml-sucks.html

http://www.podval.org/~sds/tool.html

http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/mac_destructuring-bind.html


arithmetic examples from http://www.podval.org/~sds/ocaml-sucks.html

" (/ (- (* q n) (* s s)) (1- n))

(q * n - s * s) / (n - 1)

(Int64.to_float (Int64.sub (Int64.mul q (Int64.of_int n)) (Int64.mul s s))) /. (float n)

The above looks horrible even if you open Int64:

(to_float (sub (mul q (of_int n)) (mul s s))) /. (float n)

which is not a good idea because of silent name conflict resolution. An alternative is to define infix operators:

let ( +

let ( -let ( *(Int64.to_float ((q *
) a b = Int64.add a b
) a b = Int64.sub a b
) a b = Int64.mul a b
(Int64.of_int n)) -(s *s))) /. (float n)

but this comes dangerously close to the horrors of "redefining syntax" (AKA "macro abuse") while not winning much in readability. "


with prefix and indent:

/ - * q n * s s - n 1

with backquote infix:

(q `* n `- s `* s) `/ (n `- 1)

or (assuming infix is lower prec, and leftmost infix on line is higher prec):

mb division should be as "primitive", since division is kinda weird anyway (div by 0 is error; also, integer division is not the same as division on reals). mb should use "/" syntax for something else (and use "div" or something for division)

with infix and indent:

    q `* n
  `-
    s `* s`/ n `- 1

in this example, it's kind of a pain for the programmer (unless aided by eir text editor) to

insight: the good thing about infix operators is they divide the operands visually, and they remind you what the operator is; this is why

  (q * n - s * s) / (n - 1)

is more readable than

  (/ (- (* q n) (* s s)) (1- n))

, because in the latter, you have a hard time visually seeing which are the two operands to /, and when you are reading the expression, you have to have a stack in your head so that you can remember to go back to the "/" operator when you get to "...s))". Note that:

http://www.podval.org/~sds/tool.html

note: that site's links to the CL hyperspec are broken; e.g. http://www.lispworks.com/documentation/HyperSpec/Body/chap-9.html became http://www.lispworks.com/documentation/HyperSpec/Body/09_.htm


should parsing depend on typing, or, if you want to constrain something more, just on whether or not something is a (non-0-ary) function, and on whether or not the operator takes fns? e.g. div(- (* q n) (* s s), - n 1) could maybe be written div(- * q n * s s, - n 1) because we know that - can't take * as its first operand

in this first example at least, this seem harder to read -- and what do we do if - is overloaded with something that does take fns? so, mb should stay away from this. still, can't help but wonder...

sds's requirements are kinda like Jasper's:

" [1]Memory management: I don't want to mess with memory allocation, so the language must provide some sort of garbage collection; also, I don't want to get segmentation faults, so no pointers should be present either (well, you cannot have garbage collection if you have pointers anyway). [2]Objects: the language must be OO. As Paul Graham wrote in his book ANSI Common Lisp: With macros, closures, and run-time typing, Lisp transcends object-oriented programming. The generic function model is preferred to the message passing one since it is more general and functional (in both senses of the word). [3]Egalitarianism: everything in the language must be a first class object, including functions (including the ability to create a function on the fly and return it as a value containing persistent local states, so closures are a must), structures, arrays &c (none of the three objects mentioned is first class in C, and only structures, renamed "classes", are first class in C++ and Java). [4]Library: I do not want to re-invent the wheel. The language must have hash-tables, lists, arrays, conditions &c standard. [5]Introspection: The program must be available as data, with as little structure as possible, but no less that that. Lisp is perfect - a program is a list. Perl and Tcl use strings - too little structure: a string is not a unit of evalutation or compilation; OpenJava? offers too much structure.

If you know of a language outside the Lisp family that satisfies all of the above requirements, please tell me! Most of the examples you will be able to come up with (like Dylan, Smalltalk, Haskell and ML) will be functional languages heavily influenced by the Lisp heritage.

Another nice looking language is Python. The biggest problem with it is that it is defined by its reference implementation, which does not do native code compilation (only byte-code compilation).

Yet another nice looking language is Ruby. It suffers from the same problems as Python (except that it does not even have a byte-code compilation).

Some people use TCL. Since it is even worse than Python, what else do I have to say?

There are probably more languages du jour defined by their unique implementations. Just ignore them. "

he seems to have another hidden criterion that languages must be compilable.


cornerstone is supposed to be a static, safer ruby:

http://github.com/RobertFischer/cornerstone/blob/master/README

The Cornerstone language provides an aggressively statically typed language with a Groovy/Ruby style scripting syntax. ....

Here are some notes:

Future extensions:

http://blog.headius.com/2008/03/duby-type-inferred-ruby-like-jvm.html

http://www.ruby-lang.org/en/about/: search_engines = %w[Google Yahoo MSN].map do

engine
    "http://www." + engine.downcase + ".com"
  end

seems a little verbose for a lambda function. how about:

map fn engine "http://www." + engine.downcase + ".com" $[Google Yahoo MSN]

or on one line

map (fn engine {"http://www." + engine.downcase + ".com"}) [Google Yahoo MSN]

or on two lines

map (fn engine {"http://www." + engine.downcase + ".com"}) $[Google Yahoo MSN]

or on three lines

map fn engine "http://www." + engine.downcase + ".com" $[Google Yahoo MSN]

with fn evaluation within the list

map (fn engine {"http://www." + engine.downcase + ".com"}) $[`(getEngineName 1) Yahoo MSN]

or

map (fn engine {"http://www." + engine.downcase + ".com"}) $[`getEngineName `1, Yahoo, MSN]

or

map (fn engine {"http://www." + engine.downcase + ".com"}) $[`getEngineName `1 Yahoo MSN]

to pass a function within a list (tree):

 $[`'getEngineName, Yahoo, MSN]

or

 $[`(getEngineName), Yahoo, MSN]

$ considers top level list elems as strs, but $$ considers all levels as strs:

$[[f g], string1, string2] = [[f g], "string1", "string2"]

but is it [f(g)] or [f,g]? if default is [f(g)], then [f,g] can force, and if default is [f,g], then [f(g)], or [`f g], can force

mb f g is default -- but then how to have lists like [var1 var2]? What does [f g var1 var2] do? do u not parse until arity of f is known? [f g var1 var2]? (no!)

on multilines its clear; one indent for arguments, another indent for embedded tree in the argument. i.e.

  f g
  var1
  var2

or

  f
    g
  var1
  var2

and if, instead u had

[f g, [a,f h], var2]

it could be

  f g
  [a,f h]
  var2

or

  f g
    a
    f h
  var2

or

  f g
    a
    f
      h
  var2

etc

seems kinda hard to read if we need arities to parse, tho. mb list items separated by commas by default except when in []s (and multilines are same as commas)?

var1,var2,var3

[var1 var2 var3]

  var1
  var2
  var3

if var2 = f(g),

var1,f g,var3

[var1 f(g) var3]

  var1
  f g
  var3

ummm, more regular would be

[var1 (f g) var3]

hmm, i like that

and, to not evaluate:

'[var1 var2 var3]

to not evaluate only (f g):

[var1 '(f g) var3]

to evaluate only var2:

'[var1 `var2 var3]

comparing to wikipedia's http://en.wikipedia.org/wiki/Lisp_%28programming_language%29#Self-evaluating_forms_and_quoting:

for lisp, wikipedia says: "If the variable snue has the value (bar baz) then `(foo ,snue) evaluates to (foo (bar baz)), while `(foo ,@snue) evaluates to (foo bar baz). "

for jasper, how about:

If the variable snue has the value (bar baz) then:

'[foo `snue] == [foo [bar baz]] ['foo snue] == [foo [bar baz]] '[foo `@snue] == [foo bar baz] ['foo @snue] == [foo bar baz]

that is, ' is "quote", ` is "antiquote" (i.e. evaluate even within quote), and @ is "evaluate this and then merge its toplevel into here". if you want to leave an antiquote within a quote, you must quote it:

'[foo '`snue] == [foo `snue] '[foo '``snue] == [foo ``snue]

and, to leave a '` in, you must add another ' (that is, for each token in the quote, a leading ` means antiquote; a leading quote is consumed; so:

'[foo '`snue] == [foo `snue] '['foo '`snue] == [foo `snue] '[foo `snue] == [foo '`snue] '['foo `snue] == [foo '`snue]

this seem dumb; not only do you have to keep in mind the level of quoting, but there's a weird base case with 1 or 0 quotes being equivalent on a token in a quoted tree

how about: one ` is eliminated upon quoting

no, how about: ' is both ' and antiquote (parity):

'[foo 'snue] == ['foo [bar baz]] ['foo snue] == ['foo [bar baz]] '[foo snue] == ['foo 'snue] '[foo snue] == ['foo snue]

so, 'foo is an unevaluated symbol. ' is just like prefixing each member of the list with ', unless it already has it, in which case that member gets a ' taken away.

Let f g = 1

['foo [f g] snue] = ['foo [f g] [bar baz]] ['foo (f g) snue] = ['foo 1 [bar baz]] ['foo [(f g)] snue] = ['foo [1] [bar baz]] ['foo '[f g] snue] = ['foo ['f 'g] [bar baz]] ['foo '(f g) snue] = ['foo '(f g) [bar baz]] '[foo [f g] snue] = ['foo '[f g] 'snue] = ['foo [f' g'] 'snue] '[foo (f g) snue] = ['foo '(f g) 'snue] '[foo ['f 'g] snue] = ['foo [f g] 'snue]

now that seems hard to read -- you have to know that you're inside a ' to know that the inner 'f and 'g in the last line mean "do evaluate", not "don't evaluate". also, what's up with the difference b/t [] and ()? why does lisp do better?

how about just: ' means quote, ` antiquote, each ` eliminates one ' if the ` is on the left of the '. ' and ` distribute over lists. left over `s mean "eval". so:

snue = [bar baz] 'snue = snue (the symbol) snue = 'snue (the symbol tree) '`snue = `snue (the symbol tree) `'snue = snue = [bar baz]

a better way of looking at it is that at eval time, 'X means: if X is a tree, apply ' recursively to each element on the top level o.w. wrap the (syntax tree for) X, such that the resulting wrapped tree "eval"s to itself.

at eval time, `X means if X was wrapped by a ', then let X' = the unwrapping of X, and eval X' o.w. eval(eval(X'))

but now u can't insert [bar baz], you can only insert their values...

??:

['foo (f g) snue] = ['foo 1 [bar baz]] '[foo (f g) snue] = ['foo '(f g) 'snue] `'['foo (f g) snue] = ['foo (f g) snue] = ['foo 1 [bar baz]] = `[foo '(f g) 'snue] = [`foo `'(f g) `'snue] = ['foo 1 [bar baz]] ``'[foo (f g) snue] = ['foo (f g) snue] = ['foo 1 [bar baz]]

actually, in lisp antiquotes within quasiquotes (commas within `s), the examples from wikipedia only work if u do (setq snue '(bar baz)); so the value of snue is a quoted expr, and is not further evaluated

in lisp, the 's do not "distribute"; the whole expr is quoted, as a single wrapped object... i think... but that model doesn't explain this result in elisp:

(setq bar 2) 2 (setq baz 3) 3 (setq snue '(bar baz)) (bar baz) (+ bar baz) 5

``((,@snue)) (\` (((\,@ snue)))) ((bar baz)) ``((,,@snue)) (\` (((\, bar baz)))) ((2))

in any case, the depth of the ,s don't matter in lisp.

`(foo ,snue) (foo (bar baz)) `(foo (,snue)) (foo ((bar baz)))

is the syntax with trees really that different from lisp? well, inside []s, u don't execute the first node and give it the rest of the nodes; but that IS what u do with (f x y), in both jasper (so far) and lisp....

more on lisp syntax: http://pschombe.wordpress.com/2006/03/17/improving-lisp-syntax-is-harder-than-it-looks/


how about:

blah means "blah" (not evaluated at all) 'blah means that the tree "blah" is not evaluated, EXCEPT that it recurses through the tree and evaluates any subtrees of a "`" node `blah = `blah

[blah] means to evaluate each element of the top level of the tree separately, but not to evaluate the tree as a whole

(blah) means to evaluate the tree as a whole (as does just "blah")

so:

(+ 1 2) = eval [[+ 1] 2] = 3 [+ 1 2] = eval [+ 1 2] = [+ 1 2] [+ 1 2,] = '[`eval [[+ 1] 2]] = [eval [[+ 1] 2]]] = [3] (second step unnecessary) [(+ 1 2)] = [eval [[[+ 1] 2]]]] = [3] (+ 1 2) = (+ 1 2) [+ 1 2] = [+ 1 2] [(+ 1 2)] = [(+ 1 2)] '[(+ 1 2)] = [(+ 1 2)]

'[`(+ 1 2)] = [eval [[+ 1] 2]] = eval(

idea from arc http://www.paulgraham.com/arcll1.html: " x.y and x:y for (x y) and (x 'y) respectively.

[+ _ 1] for (fn (x) (+ x 1)) "

"If you design a language that has car, cdr, cons, quote, eq, cond, and a notation for functions made of conses, then you've designed a dialect of Lisp, even if you didn't mean to."

"Perl lesson: pronouns. "

" CL: (cond ((a x) (princ "!") (b x)) ((c x) (d x)) (t (e x)))

Arc: (cond (a x) (do (pr "!") (b x)) (c x) (d x) (e x)) - Usually use if, which binds it: (if (a x) (car it)) "

note: "omitted the t in the default clause"

in arc, progn -> do, lambda -> fn

how to do http://docs.python.org/library/functions.html ?


this search turns up some interesting hits:

http://www.google.com/search?q=haskell+typeclass+default+type&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:unofficial&client=iceweasel-a

ML Modules and Haskell Type Classes: A Constructive Comparison

C++ templates/traits versus Haskell type classes

A Comparison of C++ Concepts and Haskell Type Classes

this looks interesting too: http://www.cs.mu.oz.au/~sulzmann/chameleon/download/haskell.html

haskell's "functor" typeclass should be called "mappable" (and "fmap" should just be called "map"). also, apparently haskell "Set" can't be an instance of functor, b/c it requires the underlying values to be in (typeclass?) Ord, so it can't satisfy Functor::fmap's signature, which is universally quantified: (a -> b) -> f a -> f b. todo: think about that

todo: read haskell typeclassopedia, gentle intro, report, wikibook, real world book, ghc extensions, SYB, Open Data Types and Open Functions

Generics as a Library: Bruno C.d.S. Oliveira, Ralf Hinze and Andres Löh

http://lambda-the-ultimate.org/node/1453

example of a fn that is much more consise in Joy than in CL: http://osdir.com/ml/lang.concatenative/2005-10/msg00004.html

" (defun recursive-times (k n) (labels ((temp (n) (if (zerop n) 0 (+ k (temp (1- n)))))) (temp n))) => RECURSIVE-TIMES (recursive-times 2 3) => 6

This recursive function can be defined in Joy as follows:

rt == [0 =] [pop pop 0] [[dup] dip 1 - rt +] ifte; "

monads do not neccvsarily commute or associate

should jasper use typeclasss with method0 like toBool, or just implict typecasting? think 8about algebraic data types

i guess haskell doesn't need CL's labels b/c they are implict in all fn defns

if node labels, edge labels, node and edge colors are represented, the fundamental tree data structure in haskell can also represented graphs. what is the difference b/t labels and colors? node labels are (globally) unique, but colors are predicates. edge labels can be either globally or locally (within the context of a shared src,dest) unique (i.e. there are two types of edge labels).

python has tuples and lists, one immutable and one not. jasper should combine these.

"

(for (= i 0) (< i 10) (++ i) (pr i)) (to i 10 (pr i)) (each x '(a b c) (pr x)) (let i 0 (while (< (++ i) 10) (pr i)))

"

resumable exceptions

how to conveniently do the python data = urllib.urlopen(url).read()

in jasper?

   data = read / urllib.urlopen url

??

not so bad actully, but mb reverse everything?!?

   data = url urllib.urlopen / read

"start with url, apply urllib.urlopen, then apply read"

  allTimes2 = (*2) map
  [1,2,3] allTimes2 = [2,4,6]

wow.. i like it so far.. but i guess the problem is that when u have a bunch of arguments, u have to look at the fn name to see what they are..

haskell code to count word occurrences:

  train = foldl' updateMap M.empty
     where updateMap model word = M.insertWith' (+) word 1 model

or, with x and v default vars,

   train = foldl' updateMap M.empty
     where updateMap x v = M.insertWith' (+) v 1 x
   train = foldl' (M.insertWith' (+) v 1 x) M.empty

so if u reverse,

   train = M.empty (x 1 v (+) M.insertWith') foldl' 

but fn should be on top if multiline

http://portal.acm.org/citation.cfm?id=586160.586165

'recipe' notation is interesting, but i wonder how it looks with functions that take more than 2 arguments

btw, in any case, parens should be forced whenever one prefix function gets "full", i.e. if f,g,h all take 2 args, you wouldn't want h(f(1,2),g(3,4)) to be expressible by

h (f 1 2 g 3 4)

in prefix u'd do

h (f 1 2) (g 3 4) h (f 1 2) $ g 3 4

want

h \ f 1 2 , g 3 4 in prefix can write: (h) (f 1 2) (g 3 4)

((h) (f 1 2)) (g 3 4)

h $ f 1 2 $ g 3 4 = h ((f 1 2) (g 3 4)) which isn't right want a delimiter to stop the grouping effect of the , without grouping to the right

h \ f 1 2 , g 3 4 h \ (f 1 2) (g 3 4) (h (f 1 2)) (g 3 4) a (h \ f 1 2 , g 3 4) (j \ f 1 2 , g 3 4) a \ h \ f 1 2 , g 3 4 ,, j \ f 1 2 , g 3 4 a \ h \ f 1 2 ,, g 3 4 , j \ f 1 2 ,, g 3 4 a (h (f 1 2) (g 3 4)) (j (f 1 2) (g 3 4)) a (h (f 1 2) (g 3 4)) / j (f 1 2) (g 3 4) a (h \ f 1 2 , g 3 4) / j \ f 1 2 , g 3 4

(2 1 f , 4 3 g / h) (2 1 f, 4 3 g / j) / a \ 5 b \ 6 c (((2 1 f , 4 3 g / h) (2 1 f, 4 3 g / j) / a ) 5 b ) 6 c

c(6,b(5,a(h((f 1 2),(g 3 4)),(j(f 1 2),(g 3 4)))))

(-b+sqrt(b-4*a*c))/(2*a)

(0 b - , (b (4 a * , c / *) - \ sqrt) / +) , 2 a * / div

(((0 b -) (b (4 a * , c / *) -) sqrt) +) , 2 a *) div

(-b , (b, 4*a*c / -) sqrt / +) , 2 a * / div

actually you don't need the right associative operator with default right assoc and commas:

(2 1 f , 4 3 g , h) (2 1 f, 4 3 g , j) a \ 5 b \ 6 c (2 1 f ,, 4 3 g ,, h) (2 1 f,, 4 3 g ,, j) a \ 5 b \ 6 c

this example is fairly readable:

the functions are the things directly to the left of grouping punctuation (',' or ')' or '\') (excepting '('). To find the topmost function, look all the way to the right. To find the function which is eating the argument that you are looking at, start at the right end of that argument and go to the right until you hit a non-( grouping punctuation, except skip over subexpressions. Parens enclose subexpressions. Commas are like putting parens around the subexprs to the left and right, which end at grouping punctuation (again, skipping subexprs). \s mean "take everything to the left of this and feed it to the next function on the right as its last argument". (equivalent: "take everything to the left of this and put parens around it").

(((2 1 f ,, 4 3 g ,, h) (2 1 f,, 4 3 g ,, j) a) 5 b) 6 c (2 1 f ,, 4 3 g ,, h) (2 1 f,, 4 3 g ,, j) a \ (5 b ,, 6 c ,, o) ((2 1 f ,, 4 3 g ,, h) (2 1 f,, 4 3 g ,, j) a) (5 b ,, 6 c ,, o)

i still like

(2 1 f ,, 4 3 g ,, h) (2 1 f,, 4 3 g ,, j) a \ 5 b \ 6 c

the best so far

am worried about abuse tho

(2 1 f) (4 3 g) h ,, (2 1 f) (4 3 g) j ,, a \ 5 b \ 6 c

actually that's not so bad. but i still think its a little better if we save the tall parens for outside. can we live w/ single commas? i think ppl will complain if i make them put in lotsa commas.

(2 1 f , 4 3 g , h) (2 1 f, 4 3 g , j) a \ 5 b \ 6 c (2 1 f , 4 3 g , h) (2 1 f , 4 3 g , j) a \ 5 b \ 6 c

ok. but how to disambiguate graphs? we could go back to [] for graphs, but now i'm enamored of the idea of having the syntax of graph creation match the syntax of syntax tree creation. perhaps that's part of the lisp magic that makes that language metaprogrammable and elegant. so, here's my proposal; if there is a non-0-ary fn in a graph, it tries to capture surrounding arguments (if there are too many, that's an error; the whole node that the fn is in is a function evaluation node). if you want surrounding args to instead be sister nodes in the tree, you gotta castrate that fn. ummm... dude... you want the parsing to depend on whether a symbol is bound to a non-0-ary fn? that's a very runtime-y thing.

ok, fine. within graph constructors, fns do not grab arguments except within blocks labeled `(). and now we don't need commas in graphs. happy?

or, can the commas disambiguate -- commas in the "top level" indicate a graph brotherhood, but within parens next to spaces indicates a fn? in other words, the presence of two non-comma tokens separated by spaces means that they relate to each other as with function evaluation. then, within fn evaluation, further parens are fn parens, and commas are separators.

so

[y x f , 3] [y (2 3 g) f, 3] [y (2 6 h , 3 2 j , g) f, 3]

all make sense. for the other way,

[`(y x f) , 3] [`(y (2 3 g) f), 3] [`(y (2 6 h , 3 2 j , g) f), 3]

err.. it's hard for the reader to pick out the executing subexpressions. how about a little of that terminator syntax, with some extra sauce to show that it's not really a terminator?

[![y (2 6 h , 3 2 j , g) f] 3]

aww heck, now that looks like a terminator, fine, i'll just use curly braces:

[{y (2 6 h , 3 2 j , g) f} 3]

great, but that's a pain. also, now {} cant be used for type annotations.

how about, if the reader is used to seeing commas and spaces in fns, then let commas mark fns within graphs, rather than commas marking non-fns:

[y,(2 6 h , 3 2 j , g),f 3] [y,((2,6,h),(3,2,j),g),f 3] /. if we ban spaces even within subparens of fns in graphs; no ambiguity removed, but easier to read (but harder to write) ./ [y((2,6,h)(3,2,j)g)f 3] # removed some unnec commas [(y,(((2,6)h),(3,2)j)g)f 3] # compare with usual (algol? c?) style fn call syntax [(y, (((2, 6)h),(3, 2)j)g)f 3] # compare with usual (algol? c?) style fn call syntax, with usual spaces after commas

(2 1 f , 4 3 g , h) (2 1 f, 4 3 g , j) a \ 5 b \ 6 c [((2,1,f)(4,3,g)h)((2,1,f)(4,3,g)j)a\5,b\6,c 3 "apple" ("car" [2])]

how would u say it in lisp?

'(`(c 6 (b 5 (a (j (g 4 3) (f 1 2)) (h (g 3 4) (g 1 2))))) 3 "apple" ("car" (t 2)))

in python?

[c(6,b(5,a(j(g(4,3),f(1,2)),h(g(3,4),g(1,2))))),3,"apple",["car",t(2)]] [c(6,b(5,a(j(g(4,3), f(1,2)), h(g(3,4), f(1,2)) ))), 3, "apple", ["car",t(2)]]

side by side: [((2,1,f)(4,3,g)h)((2,1,f)(4,3,g)j)a\5,b\6,c 3 "apple" ("car" [2])] '(`(c 6 (b 5 (a (j (g 3 4) (f 1 2)) (h (g 3 4) (g 1 2))))) 3 "apple" ("car" (t 2))) [c(6,b(5,a(j(g(3,4), f(1,2)), h(g(3,4), f(1,2)) ))), 3, "apple", ["car",t(2)]]

mb within graphs, [] is grouping and () is terminator, providing more ways to visually distinguish code:

[((2,1,f)(4,3,g)h)((2,1,f)(4,3,g)j)a\5,b\6,c 3 "apple" ["car" (2)]]

if {} is terminator, it's even better:

[((2,1,f)(4,3,g)h)((2,1,f)(4,3,g)j)a\5,b\6,c 3 "apple" ["car" {2}]]

[((2,1,f)(4,3,g)h)((2,1,f)(4,3,g)j)a\5,b\6,c 3 "apple" ["car" {2}]] '(`(c 6 (b 5 (a (j (g 3 4) (f 1 2)) (h (g 3 4) (g 1 2))))) 3 "apple" ("car" (t 2))) [c(6,b(5,a(j(g(3,4), f(1,2)), h(g(3,4), f(1,2)) ))), 3, "apple", ["car",t(2)]]

but spaces should be allowed in multiline lists:

[((2,1,f)(4,3,g)h)((2,1,f)(4,3,g)j)a\5,b\6,c 3 "apple" ["car" {2}]] [ $$ (2 1 f , 4 3 g , h) (2 1 f, 4 3 g , j) a \ 5 b \ 6 c 3 "apple" ["car" {2}]

let's have $$ be like $$[], and $, that's like

note that listness wasn't recursive, i.e. the []s around ["car" {2}] were not omitted hmmm.. it could be if $ cancelled ($$ str, $$$ code)

[ $$$ (2 1 f , 4 3 g , h) (2 1 f, 4 3 g , j) a \ 5 b \ 6 c $3 $"apple" "car" {2}

nah, ugly. but if it is recursive,

[ $$ (2 1 f , 4 3 g , h) (2 1 f, 4 3 g , j) a \ 5 b \ 6 c 3 "apple" "car" {2}

then if we want each subelement except for the first to be a list, we gotta make the other elements look different than the list with multiple elements:

[ $$ (2 1 f , 4 3 g , h) (2 1 f, 4 3 g , j) a \ 5 b \ 6 c [3] ["apple"] "car" {2}

well, i think that looks clean enough. but

[ $$ (2 1 f , 4 3 g , h) (2 1 f, 4 3 g , j) a \ 5 b \ 6 c [3] ["apple"] ["car" {2}]

would mean the same thing anyhow, so ppl could do that if they want.

auto list recur allows you to do

[ fruits= apple color=red price=1 banana color=yellow price=2 vegetables= carrot color=orange price=3 celery color=green price=4

btw note that the

is omitted, b/c the fact that a name is on one line, and a sublist is on the next, makes it clear that this is a node name (wrong, it could be a function). ok, so...

[ fruits= apple

	    color=red price=1
	banana |
	    color=yellow price=2
    vegetables=
	carrot |
	    color=orange price=3
	celery |
	    color=green price=4

ok, i guess i like this. quirky, but not as much as perl.

now, $ means "stringify" and $$ means "codify". at the beginning of a multiline list line, $ means "enclose the tokens in dbl quotes", and $$ means "this line is code" (only EOL separates list elems). $[] is like applying $ recursively throughout the list (` stops), and $$[] is similar (only EOL separates list elems). inside strings, $$ escapes out code: "2 + 2 = $$(2+2)"; "Bob's name is $$bobsName". in code $symbol means "symbol", i.e. lookupPhoneNumber($bob) is short for lookupPhoneNumber("bob"). oooh, better: $bob means, "lookup from resource 'bob', or use 'bob' if it doesn't exist". Case is preserved if 'bob' is used. And $"" means "lookup resource for this string and use it if it exists, otherwise use this string. String resources are the concatenation of the minimal # of words needed to distinguish, with numbers added at the end in case of duplicates.

no, better: all strings use the resource mechanism.

aarg, i somehow deleted a whole buncha stuff and can't undo. mb "redo.el" is not 4 me :[

i'll try to remember.

infix operators can only be defined in the std library. no infix precedence. all infix they all left-associate (the opposite of the usual in jasper; like APL but backwards). however, altho infix ops can't be arbitrarily defined, there are "token operators", which allow you to construct infix operators out of arbitrary functions. ^ is map; "^toLower L" converts L to lowercase (this is not infix..). ^! is a "mutable map", which operates in-place rather than returning the result. ^^ is "recursive map", which recurses through the graph until a terminator is hit, and then maps the edges. ^^^ is recursive map which also operates on interior nodes. ^vanilla^^ only terminates on "vanilla" flavored terminators (dangerous, mb should disallow; mb allow "vanilla or unlabeled"? also, should object guarantee non-cycles? or should we include cycle-detection in core algs? or include it optionally?), and ^!vanilla^^ only terminates on non-vanilla flavored terminators. (shouldn't use ! in diff ways in token prefix ops..). ^\ is foldl, a binary infix token op:

0 ^\+ [1:5] = foldl (+) 0 [1..5]

^* is elementwise multiplication, like octave's .*

mb need some "lazy/strict" modifier here, too.

the idea is that common higher order functions (hofs) which have three arguments and whose first argument binds a function can be represented by token ops in order to produce infix functions which do not surprise the reader.

arbitrary functions may still be used as infix using something like haskell's `fn` (which btw is a simple token op in jasper)

terminator flavors: like {vanilla: }

language must have good basic functions for handling the following types of objects: graphs trees lists strings sets functions objects control_flows types patterns files network_resources streams STM what's an object? a struct w/ methods? what's a control flow? like a value of the IO monad? logic programming? erlang? parallel stuff? graphs: Boost::graph; see sidebar at http://en.wikipedia.org/wiki/A*_search_algorithm

also check out APL, haskell, octave, python, erlang, prolog for commonly used fns.

graph visitors: BFS, DFS, depth-limited DFS, bidirectional, iterative deepening, greedy best first, A* some of these should mb be token ops (generalization of map and map!). similarly, the treefold generalizations of folds.

"separate women from the girls and the men from the boys... and the put the girls with the boys and the women with the men" -- specifies some sort of operation on lists of objects whose type is a direct product of two boolean types -- or maybe objects with two boolean attributes, altho i think this is the same. filter, or maybe two filters and then a union.

should lookup most popular haskell fns; here's this guy's: http://www.cse.unsw.edu.au/~en1000/haskell/inbuilt.html

similarly, "sort by" should mb be a token operator?

haskell is too hard for me

haskell is too positional ex: either

disjunctive types -- no more messing with haskell's Either. instead of pattern matching on Either's various constructors, use pattern matching, extended to match types. compiler checks for an exhaustive match, just as with Either. of course, can do n-way disjunctions, don't have to build up from 2 like with Either.

need a syntax for specifying the type of graphs.

how about: predicate logic with only AND and OR operators and parens, where the variables are a graph type spec (i.e. in parens), or whether some node in the tree is of a particular type. "forall" operator to say "the nodes at all edges of this node obey this subexpression". by default, forall only applies to unlabeled edges. if nonUniqEdgeLabels, then forall can be restricted to some labels. forall can be restricted to unmatched, unlabeled "more" edges. truth denotes that the binding subgraph is guaranteed/required to be of the specified type.

examples:

[forall (int)] list of ints

[color=string] the color edge is a string, the others are unrestricted

[aa

forall (int or aa)]
  each item in the list is either int or it is a list like this one (that is, this is a graph of any shape, where all the leaves are ints)

[fruit=x, vegetable=x] the "fruit" and "vegetable" edges are of the same type

need a way to tell a fn that has some unbound default arguments but whose required arguments have all been bound to hold off and stay "partially applied" so that we can bind the keywords later (can this be the same as castration? i think mb it can..).

@@x short for @(top x).

---

can u have a stream constructor interface that is ad hoc polymorphic on its input? i guess so

http://python-history.blogspot.com/2009/01/personal-history-part-2-cnri-and-beyond.html: "One thing I'm curious about is how languages like ML influenced your design of Python. The basic types, including the unwritten rule that lists are homogenous and tuples are heterogenous, and that iterating over strings yields more strings, suggests that you had a mild taste for ML. What other languages had a marked effect on your design? Seeing as you've bought up lisp, how do you feel about that language in context?"

unshadow and parent can be unified; ^self is parent node (if ^ is unshadow; mb it should be &).

vertical and horiz matrix concatenate? graph concatenate? can the shape of matrices be represented in the graphs?


http://python-history.blogspot.com/2009/02/early-language-design-and-development.html " Numbers

The implementation of numbers, especially integers, is one area where I made several serious design mistakes, but also learned important lessons concerning Python's design.

Since Python had two different integer types, I needed to figure out some way to distinguish between the two types in a program. My solution was to require users to explicitly state when they wanted to use longs by writing numbers with a trailing L (e.g., 1234L). This is one area where Python violated the ABC-inspired philosophy of not requiring users to care about a uninteresting implementation details.

Sadly, this was only a minor detail of much larger problem. A more egregious error was that my implementation of integers and longs had slightly different semantics in certain cases! Since the int type was represented as a machine integer, operations that overflowed would silently clip the result to 32 bits or whatever the precision of the C long type happened to be. In addition, the int type, while normally considered signed, was treated as an unsigned number by bitwise and shift operations and by conversions to/from octal and hexadecimal representations. Longs, on the other hand, were always considered signed. Therefore, some operations would produce a different result depending on whether an argument was represented as an int or a long. For example, given 32-bit arithmetic, 1<<31 (1 shifted left by 31 bits) would produce the largest negative 32-bit integer, and 1<<32 would produce zero, whereas 1L<<31 (1 represented as long shifted left by 31 bits) would produce a long integer equal to 231, and 1L<<32 would produce 232.

To resolve some of these issues I made a simple fix. Rather than having integer operations silently clip the result, I changed most arithmetic operations to raise an OverflowError? exception when the result didn't fit. (The only exception to this checking were the "bit-wise" operations mentioned above, where I assumed that users would expect the behavior of these operations in C.) .... Sadly, I'm sorry to say that raising an overflow exception was not really the right solution either! At the time, I was stuck on C s rule operations on numeric type T return a result of type T . This rule was also the reason for my other big mistake in integer semantics: truncating the result of integer division, which I will discuss in a later blog post. In hindsight, I should have made integer operations that overflow promote their result to longs. This is the way that Python works today, but it took a long time to make this transition. "

"

Despite the problem with numbers, one very positive thing came out of this experience. I decided that there should be no undefined result values in Python instead, exceptions should always be raised when no correct return value can be computed. Thus, Python programs would never fail due to undefined values being silently passed around behind the scenes. This is still an important principle of the language, both in the language proper and in the standard library. "

Boo

u should be able to add attributes to an object, but as noted previously, under the covers this subclasses the class so that this can be typechecked. however, callers who don't know about the subclass see it as their original class. that is to say, you can have a universal (not exisential) type signature and then, actually, receive an object of that class and return one of a subclass -- and no one will be the wiser. the attributes you add will be hygenic, of course, to prevent name collisions.

superclassing can be done via a ___super attribute (attr. = labeled edge) (in the node itself, or in the type?). i guess this is preferable to a 'prototype' system b/c the storage of the super's attributes can be done just once, saving memory -- but i'm not sure.

type of a node can be made available in a ___type labeled edge. i guess the types themselves are also represented as graphs, which annotate a graph and mark off guaranteed types on it. no need to terminate there, i suppose -- the types may themselves be subgraphs. one attribute of a type is whether it is a "client's choice" (universal) or "my choice" (exisential).

need a notation in types for asserting that different lists have the same number of positional elements -- so that you can represent a matrix.

if matrixes are represented as a list of lists, where the inner list is a row, then need concise notation for defining the matrix operation matr.:.3 to get the third column. well, actually, mb that's already all u need : means, "map the following selector over each positional element of this list, and make a list of the result".

btw i often say 'list' instead of 'graph' just b/c it has less letters. also, i've been assuming throughout that any node can be transparently replaced by an object -- so traversing a graph may involve covertly calling object methods that dynamically construct it, and the shape of the graph need not correspond to how the information is stored.

mb all vars should be "universal but with subclassing", i.e. if u create an object of one type and the caller wants one of a different type, then u both see what u want. but wait, this won't work if both of you want to redefine the same nodes, right? so ur hidden subclassing can add edges, and mb advise (subclass) methods but it must call super. but then how do u decide the order of the advising chain? hmmmm.

btw so "jasper core" is shaping up to be a graph, with evaluation proceeding until a terminator is hit (graph constructor), and within lists, an "eval" (ev) may be hit to signal code embedded within the graph. most code is compiled, and compiled code cant be modified at runtime, but if there is a runtime call to eval, that's ok, the jasper interpreter will be linked in (although the runtime eval can't affect the types of variables through which it interacts with the compiled code). the type of a function is a list of positional arguments (btw, every node, fn or not, needs a counter that measures how long the subset of the list which is 'positional' is, so that the positional items can also have edge labels, but not all attributes are counted when, e.g., len is run on the obj).

we want edges to support multiple labels, so should the edge label just be itself a graph? but then it may trespass on the fn of the reified edges. why not just (in the abstract representation, if not the implementation) just put the edge labels in the reified edge. obvious operations on nodes are "give me a list of edges that go out of this node, and have this label", and "give me a list of edges that go out of come into this node, and have this label"; the . selector is a specialization of the latter that picks one edge in an undefined fashion when there are multiple such. now edge attributes, colors, and local labels are all the same thing.

heck, should we just generalize this to multigraphs and let edges (now, just special nodes) represent arbitrary finite relations between other nodes? sure, and we should provide relation ops like closure in the basic library, but it seems like a graph is still the right basic abstraction. a relation can be implemented as a node with a list of lists. but we need attribute lookup, which is following a graph edge. is the "relation node" similar to an "index node"? with the actual relation tuple nodes in the role of edge nodes? i think so. so, we can access a relation named 'relname' using the syntax "g..relname". well, extending this further, instead of binary values on tuples of nodes, how about node values? want g..grelname.[a b c] to return a node. for ordinary relations, it returns a bool. that fits in with sets of edge labels being arbitrary graphs stored in the edge nodes. because then each edge label could be an arbitary graph, in this case a list of edges. in general, edge traversal then involves running eq on all edge labels. under the covers, the relation typeclass implementations will of course do things more efficiently.

now we need a notation for types to indicate constraints on the types of their edge nodes, and on their labels. how about just graph constructors, and u can put a type name wherever you would put a node name before. this lets us specify edge types with the same syntax as for edge names.

btw if labels can be graphs, then edge label names might be treated as symbols, no? either write $name to treat it as a string, or by default make it a string and use ` to eval as a symbol. the former is cleaner but will lead to everyone having to type $ too much, so let's do the latter.

so then to specify an edge type, use `type=... no, b/c what goes after the =s? ok, less pretty, but just use the ___edge_label_type edge, which is magical and mystical b/c if you put a type name there, it actually sets that to the type, rather than requiring that ___edge_label_type is a type.

note: when graphs are "merged" by connecting one object to another, what happens to node label clashes? mb index nodes are lexically scoped, but there is just "one big graph" denoting memory

. "nodes pointing TO this node" shouldn't be possible IN GENERAL (except as a debugging tool), but rather should be w/ respect to a particular index node -- that is, if some guy out of your scope has a pointer to a value that u also have, u shouldn't know about that. index nodes can be manually merged.

mb i should be saying "node" instead of "graph".

unlike haskell, don't have to declare all constructors in the type declaration (at least, not for interface types; b/c our interfaces are like haskell's typeclasses). our "dijoint types"/"union types" will be disjunctions of types, not disjunctions of constructors.

here's how a fn could be represented as a node. there is a list representing argument slots. some of them have normal values, and some have n (if the default value IS n, then it is 'escaped' to nn, etc. there is an infinite series of ns, isomorphic to the natural numbers, and a facility to do arithmetic with them (basically, they act like natural numbers). n ~= 0, nn ~= 1, etc. n+1 is 'escaping' n. there is a fn escNil that is n+1 if it's n, and id otherwise). if a list position has n, that means it must be filled before the fn can be executed. fn application is graph assignment; the unlabeled items from the source are put in order into the positional arguments in the fn, and labeled items are matched to the appropriate keywords. if there are too many unlabeled source items, it is an error; if there are too few source items to fill the non-default dest items, the end result is a partially applied fn; if the non-default dest items are filled and there are not too many unlabeled source items, the graph in the __call attribute is executed (the uncompiled graph is only included in the executable if it is/might be referenced) -- unless the fn call specified to neutralize the fn, in which case it stays partially applied for now (b/c mb someone will want to override some default args later).


gvr explains why python uses self. for instance vars:

http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html

" Development of the class Syntax

Having designed the run-time representations for user-defined classes and instances, my next task was to design the syntax for class definitions, and in particular for the method definitions contained therein. A major design constraint was that I didn t want to add syntax for methods that differed from the syntax for functions. Refactoring the grammar and the byte code generator to handle such similar cases differently felt like a huge task. However, even if I was successful in keeping the grammar the same, I still had to figure out some way to deal with instance variables. Initially, I had hoped to emulate implicit instance variables as seen in C++. For example, in C++, classes are defined by code like this:

class A { public: int x; void spam(int y) { printf("%d %d\n", x, y); } };

In this class, an instance variable x has been declared. In methods, references to x implicitly refer to the corresponding instance variable. For example, in the method spam(), the variable x is not declared as either function parameter or as local variable However, since the class has declared an instance variable with that name, references to x simply refer to that variable. Although I had hoped to provide something similar in Python, it quickly became clear that such an approach would be impossible because there was no way to elegantly distinguish instance variables from local variables in a language without variable declarations.

In theory, getting the value of instance variables would be easy enough. Python already had a search order for unqualified variable names: locals, globals, and built-ins. Each of these were represented as a dictionary mapping variable names to values. Thus, for each variable reference, a series of dictionaries would be searched until a hit was found. For example, when executing a function with a local variable p, and a global variable q, a statement like print p, q would look up p in the first dictionary in the search order, the dictionary containing local variables, and find a match. Next it would look up q in the first dictionary, find no match, then look it up in the second dictionary, the global variables, and find a match.

It would have been easy to add the current object s instance dictionary in front of this search list when executing a method. Then, in a method of an object with an instance variable x and local variable y, a statement like print x, y would find x in the instance dictionary (the first dictionary on the search path) and y in the local variable dictionary (the second dictionary).

The problem with this strategy is that it falls apart for setting instance variables. Python s assignment doesn t search for the variable name in the dictionaries, but simply adds or replaces the variable in the first dictionary in the search order, normally the local variables. This has the effect that variables are always created in the local scope by default (although it should be noted that there is a global declaration to override this on a per-function, per-variable basis.)

Without changing this simple-minded approach to assignment, making the instance dictionary the first item in the search order would make it impossible to assign to local variables in a method. For example, if you had a method like this

def spam(y): x = 1 y = 2

The assignments to x and y would overwrite the instance variable x and create a new instance variable y that shadowed the local variable y. Swapping instance variables and local variables in the search order would merely reverse the problem, making it impossible to assign to instance variables.

Changing the semantics of assignment to assign to an instance variable if one already existed and to a local otherwise wouldn t work either, since this would create a bootstrap problem: how does an instance variable get created initially? A possible solution might have been to require explicit declaration of instance variables as was the case for global variables, but I really didn t want to add a feature like that given that that I had gotten this far without any variable declarations at all. Plus, the extra specification required for indicating a global variable was more of a special case that was used sparingly in most code. Requiring a special specification for instance variables, on the other hand, would have to be used almost everywhere in a class. Another possible solution would have been to make instance variables lexically distinct. For example, having instance variables start with a special character such as @ (an approach taken by Ruby) or by having some kind of special naming convention involving prefixes or capitalization. Neither of these appealed to me (and they still don't).

Instead, I decided to give up on the idea of implicit references to instance variables "

since jasper is statically typed, and since we want to keep track of state-bearing variables anyhow, i choose instead to require declarations for instance vars (i.e. mutable variables carrying persisent state).

__init for constructors

"macros such as __FILE__ in the C preprocessor"

compiler warning for series of pattern matches that aren't comprehensive

compiler "normalization" option: consolidate/move module-level decls to module decl if any item in a list is a singleton list, then put explicit []s around all list items mb: replace parens with commas where appropriate ? consolidate/move var decls to top of their scope

http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html could mb use another read sometime

http://python-history.blogspot.com/2009/02/first-class-everything.html

and read everything else there (not read yet), starting with http://python-history.blogspot.com/2009/03/how-everything-became-executable.html

read the rest of: http://www.paulgraham.com/arcll1.html (i'm somewhere just after the figure "Arc's 4 basic iterators: ")

don't have to reread, but the rest of this blog is probably good: http://pschombe.wordpress.com/2006/03/17/improving-lisp-syntax-is-harder-than-it-looks/ e.g. see these, which i haven't read: http://pschombe.wordpress.com/2006/05/11/type-systems-and-scheme-summary/ http://pschombe.wordpress.com/2006/04/30/real-criticisms-of-scheme/ http://pschombe.wordpress.com/2006/05/06/the-next-big-programming-idiom/

also check out clojure, scala, verilog

toread: http://www.google.com/#hl=en&source=hp&q=lisp+quasiquote&aq=0&aqi=g1&oq=lisp+quasi&fp=1&cad=b e.g. ropas.snu.ac.kr/~kwang/talk/popl06.pdf repository.readscheme.org/ftp/papers/pepm99/bawden.pdf

more on implicit coercion in perl: http://www.perlfoundation.org/perl5/index.cgi?prototype http://enfranchisedmind.com/blog/posts/a-defense-of-prototypes/

quick haskell example: http://enfranchisedmind.com/blog/posts/i-think-my-brain-just-exploded/

http://www.podval.org/~sds/ocaml-sucks.html

http://www.reddit.com/r/programming/comments/l434/ask_reddit_erlang_haskell_ocaml_which_functional

http://www.reddit.com/r/programming/comments/l434/ask_reddit_erlang_haskell_ocaml_which_functional "#

I think of O'Caml as, at the very least, a better than C. Take its performance, take its portability, take even its networking interface: the very same [unix] C has, with all of the same logic and and difficulties, but with an amazingly better language to manage these with.

Haskell-I-mean-GHC had a similar 'low level' socket library and then a broken-by-design 'high level' networking library, the latter trying to pretend that sockets are files. Erlang, contrawise, has the world's best-thought-out networking library -- a wonderfully useful abstraction over BSD sockets that doesn't fall over the way GHC's does.

Seriously, sockets are not files. They aren't generic streams only distinguishable from files in their origin. You can't even correctly implement something as trivial as an echo server by pretending otherwise."

"

"i think the way lazy evaluation lets you create new syntax via functions in Haskell is even weirder than Scheme's pattern-matching based macros which is in turn weirder than Common Lisp's macro system."

"That's probably the biggest reason that I prefer Dylan, which has an algol-like syntax expressing pretty much the same semantics as Scheme, with a cleaned-up and slimmed-down version of Common Lisp's standard library."

"Also, a lot of design concerns that are taken for granted in the OO community, like that small syntactical changes shouldn't change the a program from one valid interpretation to another (look at = and == in C), that verbosity is better for readability than symbols (look at APL) and that standard ways of doing things are crucial for readability (look at perl and shiver!). FPL designers and users tend to take lightly on these things, to put it mildly."

the relational modeling language alloy.

"*

JulianMorrison? 6 points7 points8 points 3 years ago[+] (8 children)

JulianMorrison? 6 points7 points8 points 3 years ago[-]

Or to put it another way, the cleverness of monads in Haskell is: an impure function which changes the world can be viewed as a pure function which is passed a world, and returns it changed. "

"qwe1234 comment score below threshold[+] (7 children)

qwe1234 -9 points-8 points-7 points 3 years ago[-]

you don't need 'monads' (or anything else for that matter) to thread state throughout your program.

in fact, not using 'monads' or other braindead unnecessary abstractions will only make your code simpler, shorter, and easier to maintain.

ayrnieu -1 points0 points1 point 3 years ago[+] (4 children)

ayrnieu -1 points0 points1 point 3 years ago[-]

Sure. In this case, the IO monad guarantees the single-threading.

qwe1234 comment score below threshold[+] (3 children)

qwe1234 -7 points-6 points-5 points 3 years ago[-]

elaborate what exactly you mean by 'single-threading'.

ayrnieu -4 points-3 points-2 points 3 years ago[+] (2 children)

ayrnieu -4 points-3 points-2 points 3 years ago[-]

frobnicate(Frob0) -> Frob1 = frobnicate(vision,Frob0), Frob2 = frobnicate(hearing,Frob1), Frob3 = frobnicate(smell,Frob2), Frob4 = frobnicate(touch,Frob3), Frob5 = frobnicate('magneto-sense from embedding really powerful magnets in your fingertips',Frob4), Frob5.

This single-threads Frob through frobnicate/1, which may copy Frob five times to make five changes. An automatic optimizer might safely turn this sequence of five non-destructive changes into one copy and five destructive changes. An automatic optimizer that considered my entire program as a whole might elid the copy, to always destructively modify Frob. Haskell programs, which take the entire state of the universe as a parameter, always benefit from this optimization.

Saving the costly 'copy'. This is the joke.

qwe1234 -3 points-2 points-1 points 3 years ago[+] (1 child)

qwe1234 -3 points-2 points-1 points 3 years ago[-]

and what do intelligent compiler optimizations have to do with 'monads' or 'category theory'? (---> nothing. you don't even need functional programming to do the sort or thing you described, it is purely a compiler construction issue.)

ayrnieu -4 points-3 points-2 points 3 years ago[+] (0 children)

ayrnieu -4 points-3 points-2 points 3 years ago[-]

Sure. As I said, the IO monad guarantees the single-threading. I suppose that a compiler (and a human) has more ease about determining that a program does not abuse a monad that in determining that a program such as mine never accidentally returns Frob4, or never serializes Frob2 to disk, or such.

alextp -2 points-1 points0 points 3 years ago[+] (1 child)

alextp -2 points-1 points0 points 3 years ago[-]

I was about to downmod this without reading to the end, but I didn't, and stand corrected. qwe1234 didn't troll this time!

qwe1234 comment score below threshold[+] (0 children)

qwe1234 -7 points-6 points-5 points 3 years ago[-]

i'm for shorter code with less talking.

you don't really need to invent abstract notation to program well. (paradoxical, but true.)

in other words, what you people call 'theory' has a right to exist in only two cases: a) for explaining natural phenomena and b) as a tool for getting analytical results you wouldn't be able to express otherwise.

'monads' don't help at all in either a) or b), and thus i don't think they have justified their existence.

davids 8 points9 points10 points 3 years ago[+] (3 children)

davids 8 points9 points10 points 3 years ago[-]

Learn Oz (http://www.mozart-oz.org/) with Concepts, Techniques, and Models of Computer Programming (http://www.info.ucl.ac.be/~pvr/book.html) and the others come much easier. More importantly the text gives you an understanding of tradeoffs and languange design decisions.

lost-theory 5 points6 points7 points 3 years ago[+] (2 children)

lost-theory 5 points6 points7 points 3 years ago[-]

I'll second the idea of learning Oz / Mozart. The CTM textbook (which you can still find for free online) is a modern interpretation of SICP, teaching you how to use the language to make 'new languages'. The official tutorial isn't bad for learning either. The syntax of Oz is a little weird, but not very difficult to learn; I've already got my head wrapped around most of it. Only problem with the language I would say is that the community is small and libraries are lacking, but that's often the case with the "weird" Functional languages.

The reason why it's a good language to learn is that it is a general purpose language that can be extended to fit any paradigm. Functional programming is fairly natural to do (if you avoid procedures), it has very good built in concurrency mechanisms, performance is decent (especially with concurrency, see here on the Shootout). Check it out!

j was created by iverson after apl: http://www.jsoftware.com/

i'm up to: http://www.jsoftware.com/help/primer/vocabulary.htm

so far i like the fns but don't like the idea of having one word be a "homonym" for things with such different meanings (logical OR vs GCD? mb "set union" but how often do we need the gcd?), and i don't like the basic meaning of the fn to change depending on how many arguments it gets (+ is + if two arguments, but conjugate with 1 argument?)

EDIT: For 'different', I'd also recommend Oz, Mercury, Prolog, Q, N-dimensional time-travelling befunge...


http://pypi.python.org/pypi/strait

" For instance the languages Fortress and Scala use the name trait but with a different meaning (Scala traits are very close to multiple inheritance). For me a trait is a bunch of methods and attributes with the following properties:

   1. the methods/attributes in a trait belong logically together;
   2. if a trait enhances a class, then all subclasses are enhanced too;
   3. if a trait has methods in common with the class, then the methods defined in the class have the precedence;
   4. the trait order is not important, i.e. enhancing a class first with trait T1 and then with trait T2 or viceversa is the same;
   5. if traits T1 and T2 have names in common, enhancing a class both with T1 and T2 raises an error;
   6. if a trait has methods in common with the base class, then the trait methods have the precedence;
   7. a class can be seen both as a composition of traits and as an homogeneous entity.

Properties from 4 to 7 are the distinguishing properties of traits with respect to multiple inheritance and mixins. In particular, because of 4 and 5, all the complications with the Method Resolution Order disappear and the overriding is never implicit. Property 6 is mostly unusual: typically in Python the base class has the precedence over mixin classes. Property 7 should be intended in the sense that a trait implementation must provide introspection facilities to make seemless the transition between classes viewed as atomic entities and as composed entities. "


http://bartoszmilewski.wordpress.com/2009/07/16/on-actors-and-casting/

" Messages in Kilim are restricted to objects with no internal aliasing, which have move semantics. "


select for multiple mailboxes, e.g.

" int n = Mailbox.select(mb0, mb1, .., timeout);

The return value is the index of the mailbox, or -1 for the timeout. "


http://bartoszmilewski.wordpress.com/2009/07/16/on-actors-and-casting/

" Dynamic Networks

Everything I described so far is common to CSP (Communicating Sequential Processes) and the Actor model. Here s what makes actors more general:

Connections between actors are dynamic. Unlike processes in CSP, actors may establish communication channels dynamically. They may pass messages containing references to actors (or mailboxes). They can then send messages to those actors. Here s a Scala example:

receive { case (name: String, actor: Actor) => actor ! lookup(name) }

The original message is a tuple combining a string and an actor object. The receiver sends the result of lookup(name) to the actor it has just learned about. Thus a new communication channel between the receiver and the unknown actor can be established at runtime. (In Kilim the same is possible by passing mailboxes via messages.) "


what sucks about erlang:

http://damienkatz.net/2008/03/what_sucks_abou.html

--- havent read this, just saw it in a web search, dunno if it's good or not:

www.fscript.org/documentation/OOPAL.pdf


http://www.csm.ornl.gov/pvm/

http://marijn.haverbeke.nl/monad.html

http://www.alsonkemp.com/haskell/reflections-on-leaving-haskell/

---

" Surely, if you were actually building a programming environment, rather than trying to express the fundamentals of mathematics, your main concern would be how to represent code as data, rather than the other way around. (Again, this is the Lisp approach.)

Defining data as dynamic higher-order functions worked badly when most computing was standalone. And it works even worse now that we have these things called networks. The problem is that a dynamic higher-order function is a sort of ethereal construct, a speck of four-dimensional pixie dust, and the idea of saving it to disk or sending it over the network does not make any particular sense. " -- http://www.cppblog.com/cfmonkey/archive/2008/07/31/57671.html


"autovivication"

The debugger session below illustrates autovivification of a hash:

  DB<1> $h{A}{B}{C}{D}=1
  DB<2> x \%h                                                                  
   0  HASH(0x83c71ac)
   'A' => HASH(0x837d50c)
      'B' => HASH(0x83c71e8)
         'C' => HASH(0x83c7218)
            'D' => 1
  DB<3>

Hashes several layers deep were created automatically without any declarations


http://enfranchisedmind.com/blog/posts/post-functional-scala/

\xab Going to be at Mid South Software Symposium (Memphis, TN) Worse is not better! \xbb Let s start with some background.

I complained that Scala did not seem to be very functional to me, but I didn t really know how best to express what was fundamentally wrong with it. I did know that if functional languages have a fixed set of features like Scala s creator, Odersky, claims, then it wasn t simply first-class functions in there, function literals, closures , types, generics, [and] pattern matching . Scala has missed the functional boat in some basic way.

After a kerfuffle in the comments, Brian enlightened us all by telling us what is a functional programming language. His explanation (while being a self-admitted generalization) is summarized as follows:

    So, what is it that differentiates the functional programming languages from all the other programming languages? It is simply this: the functional programming languages use, as their fundamental model of computation, the lambda calculus, while all the other programming languages use the Turing machine as their fundamental model of computation.

Six months later, Odersky responds with a very interesting post, which actually agrees that Scala is not a functional language in Brian s sense, but instead argues that any language is functional if it makes programming centered around functions easy and natural . He then runs through a list of features which is in common with functional languages, noting that Scala has them within handwave enough (more on that later). He ends wishing that people would stop thinking of functional programming as a different, novel, or exotic way to code . Even more, though, Scala is apparently an early example of a new breed of postfunctional languages . And that gets us to this blog post.

First of all, Odersky is still missing the point. It s not about whether you use fold, map, and iter, or whether you can write closures easily. It s not even really about pure functions vs. side-effects. To code in a functional style is a fundamentally different way of thinking about problems: instead of thinking about problems as nouns that are doing things, functional programming views a problem as a series of transformations to the world which results in an answer. This is why functional programming is considered a different, novel, or exotic way to code : it is a different, novel, and (as of yet) exotic way to code. It s as different, novel, and exotic from OO as OO was from procedural. It s a different way of thinking about the entire issue. You can check out this snippet of an IRC conversation from #ocaml for more on that.

The paragon of this way of programming is point-free programming, where you are quite literally building up a mega-function that describes how your program works, and then executing that one, single function when you run that program. If your language doesn t lead people to re-discover point free programming at least in the small, then the language really isn t taking function manipulation and functional language type conceptions seriously. And that s the case with Scala: even Odersky admits that in Scala, currying is more verbose and much less used than in other functional languages . (Protip to Scala people: If one of the fundamental stunts of a style is pervasive in all the code but yours, you re not in the same style of programming.)

What really gets me, though, is the claim that Scala is an early example of a new breed of postfunctional languages , because aside from the static typing, all the language features that Odersky trots out already exist in Perl. It s hard to be a vanguard of a new breed of programming languages when there s prior art from the 1980s.

Don t believe me? The existence of a book on the topic unconvincing? Then let s run the list of functional language features from Odersky.

      sub apply(&$) {  # Take a function as an argument no problem
        $_[0]->($_[1]);
      }
       
      sub times2($) {  # Create a function to take
        print $_[0]*2 . "\n";
      }
       
      apply(\&times2, 3);
      my $x = 2;
      apply { print $_[0]*$x . "\n" } 3;
       
      my $times_x = sub($) {
        print $_[0]*$x . "\n";
      };
      $times_x->(3);
      sub add {
        my $x = shift;
        return sub { $x + shift };
      }
      add(2)->(3);  # Okay, so you do need an extra ->
      In the case of our apply function above (where we take a function as the first argument), it s even easier.
      apply { print $_[0]*$x . "\n" } 8;
      Now, there isn t really argument skipping (i.e.: foo(_:Int,3)) as a syntax feature, and there isn t a built-in curry function, but if you want Scala s Function.curried in perl, here it is:
  1. This code released under Creative Commons 0 and WTFPL. sub curry(&@) { my($f,@args) = @_; return sub { $f->(@args, @_); }; }
      sub add($$) {
        return $_[0] + $_[1];
      }
       
      my $curried = curry(\&add, 2); 
      print $curried->(3) . "\n";

In addition, perl s got a few features in its favor for functional programming, like more flexible arguments, autovificiation, list/argument coercion, and dynamic symbol table mangling. Since perl also has OO capabilities, perl is at least as convincing a post-functional language as Scala. But there s even more in common between the two than that.

Odersky s post-functional language is really a subtype of Larry Wall s post-modern language : it s an attempt to create a language that is a grab-bag of multiple paradigms. And when you do that, you re just begging for the complaints that you hear leveled against both perl and Scala: it s too complicated, its syntax is weird, it s too magical, people write in entirely distinct subsets of the language, etc. (cite, cite, cite) Now, those who master the language (or master their favorite subset of it) love the TIMTOWTDI aspect. But it also means that the language is left as a jack-of-all-trades, master of none. Yes, Scala and perl integrate a lot of powerful tools from functional languages but learning OCaml still blew my mind, despite knowing perl for years. As I started off saying, Scala is not a functional programming language. It is a statically typed object oriented language with closures.

Now, there is a sense in which Odersky is really onto something. The world of programming is forever transformed with closures and list comprehensions as being mandatory for new high-level languages, even if they re otherwise object oriented. And software developers are going to need how to work with them effectively if they want to read code moving forward. Yes, after 20+ years, the rest of the programming world finally caught up to one of perl s fundamental insights. This entry was posted in Classic, Perl, Programming Language Punditry and tagged fp, Perl, Scala. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.


Go is like ALGOL:

http://www.cowlark.com/2009-11-15-go/


readable notes on one pass of an ML compile (i guess other wiki pages cover other passes) http://mlton.org/Defunctorize

---

http://supertech.csail.mit.edu/cilk/

---

a specific anti-pattern I ve detected in a large number of languages.

Let me explain what I m talking about using a concrete example. Go has pointers. Why? I mean, even Java has referential transparency. The advantage that pointers bring is they give the programmer the ability to control whether or not a given object is boxed (i.e. allocated on the heap as a seperate object) or not. But this is just an optimization- changing an object from being boxed to being unboxed (or vice versa) is an optimization. And not a very important one, at least if you have a copying GC.

-- http://enfranchisedmind.com/blog/posts/my-question-on-go/

---

"

Robert: Google has two primary use cases for Go:

The first is to replace C++ for internal services and middleware that make heavy use of Protocol Buffers. Impedance mismatch with protobufs is probably the primary reason that Go didn t ship with heterogeneous lists or discriminated unions (much less GADTs). Go is extraordinarily well-suited to Google s service idioms.

The second is for writing code that targets NaCL? in the browser (which ships with Chrome), where you compile to a safe/sanitized subset of word-aligned machine code. Go s runtime/libc is tiny compared to any other language of it s expressivity, and the CSP model is very good for handling things like DOM events, much better than interrupts/signals.

" http://enfranchisedmind.com/blog/posts/my-question-on-go/#comment-37026

---

The biggest thing Go nicked from Haskell is type classes aka interfaces in Go but in a dynamic sense rather than a static sense. It s Go s primary innovative feature.

My own problems with Go lie more in the direction of what it lacks the oddness over the magic make(), dodgy confusion with arrays and slices, lack of generics and exceptions but the need for an efficient but far less verbose language than Java seems pretty clear to me. Particularly needed are decent linkage with C, and prescriptive control over record layout. D plays in the same space.

The green threading (CSP) model is also hard to work efficiently in one of the common runtimes, e.g. CLR or JVM.

 -- http://enfranchisedmind.com/blog/posts/my-question-on-go/#comment-37013

---

Your criticism of Go only makes sense if you completely ignore Goroutines CSP is a hell of an abstraction, especially since its concurrency is targeted at the algorithm, not the implementation. The point is not to make your code run faster (not that there aren t Sufficiently Smart Compilers) the point is to make your code as clear and concise as possible.

Go could easily be the language to make CSP popular in industry (Erlang ain t it), especially if Google s NaCL? takes off with ChromeOS? (Go already ships with it as a compiler target).

The rest of the features are just gravy for systems programming: perfect string handling, baked-in module system, pointers without Undefined Behavior , no Classicism, no Inheritance, first-class Interfaces& The only things missing are parametric polymorphism for user types, and the ability for the user to implement the runtime s interfaces. -- http://enfranchisedmind.com/blog/posts/my-question-on-go/

---

http://code.google.com/p/protobuf/

---

Variant types are a form of polymorphism limited to the forms described at the definition site. Go s polymorphism through interfaces is more general, but seems to me to be able to provide the functionality of discriminated unions with information hiding, though without pattern matching etc.

---

Fred Blasdel Posted November 27, 2009 at 7:38 PM

Permalink

Go has machine pointers but they aren t what you think they are:

Dereferencing is automatic in common cases No pointer arithmetic is allowed No such thing as undefined behavior it is possible to manually construct an invalid pointer, but dereferencing one will always cause the runtime to halt

They re present because you re going to have to tackle the reference/value distinction somehow, and Go is intended to compile to machine code. By exposing them, the user can write tight code that making strong assumptions about how data structures are copied, and what kind of memory footprint will result.

Pointers are not just an optimization They re a lot like Tail-Call Elimination theoretically it s just an implementation detail, but it s one that has a massive impact on programming style. Any code that takes advantage of it is invalid in its absence and immune to refactoring it out.

--- http://enfranchisedmind.com/blog/posts/my-question-on-go/#comment-37038

---

Fred Blasdel Posted November 27, 2009 at 8:41 PM

Permalink
    I d be interested to know why, as you comment,  Erlang ain t it 

It combines lots of parochial weirdness from both Smalltalk and Prolog (message-passing, image-based live-in sandbox, sentential syntax, etc.) making it very inaccessible to industrial programmers. OTP s reliability is perfect, but only if you use it as Ericsson intended: it falls over easily if there s any impedance mismatch in how you architected your system.

Erlang is obviously successful, and will continue to be so for quite a long time, but just in a very narrow category of infrastructure applications.

    From where I sit, the three main advantages Go has over various competitors like D and Erlang and Cilk, is threefold: 1, Google, 2, Brian Kerninghan, 3. Rob Pike. In this sense, Go is like Arc, in that the main reason everyone is gaga over it is who is behind the language, not the language itself. If you or I had released Go, it d have disappeared without a ripple, and no one would care about it. If you don t believe me, ask Walter Bright. Who s he? Precisely.

I already knew all about Walter from having explored D, which seems to have been an obvious failure nearly from birth noone cares about a marginally less-fucked version of C++, especially not one controlled by a proprietary compiler company. Cilk manages to apply that same proprietary stink to a project that already had the innate unsuccessfulness of private academia (see Cyclone for a language that never even made that leap).

It s uncouth to apply the Arc slur to a language that shipped with two full compiler suites targeting many native architectures, with it s own complete standard library. Arc is ~100kb of shitty macros that only work on an old version of MzScheme? and break its string support.

    I am more than a little bit disturbed at the implication that the only way for a language to gain widespread adoption is to have it backed by a large corporation.

I m saying something else: that a language backed by a tiny corporation is meaningless. I know of three things that can really get a language off the ground: early total openness, backing by a megacorp, and multiple independent implementations.

    Yes, as far as I m concerned, being owned and pushed by a big corporation is a minus for a language. Languages should be open sourced and publicly standardized (ANSI, ISO, ECMA, pick one).

Have public standardization bodies ever been anything but facilitators for corporate bullying? I guess there are languages for which standardization has been a tombstone Smalltalk multiple times!

    The existing industrial languages that are otherwise suitable have tons of baggage: Scala, Clojure, Haskell, and Erlang are all idiosyncratic in their own ways, and all have major features that have nothing to do with concurrency.
        Are you seriously suggesting that Go is not idiosyncratic in it s own way?

Of course it is, but save for CSP (and maybe interfaces), its oddities are ones of omission and clarification. It is so very familiar.

    Or that every feature has to be related to concurrency? Including all of Go s features?

If you re trying to teach concurrency, it helps to be able to focus on that alone and not have to address issues that are really about the platform or syntax or evaluation idiom.

You would understand if you d ever taught Haskell to undergraduates :)

-- http://enfranchisedmind.com/blog/posts/my-question-on-go/#comment-37039

Posted November 27, 2009 at 10:33 PM

Permalink

Technically what Go has is references, like Pascal and Algol-68, not pointers like C and C++. Yes, I know this. I m old enough that they taught me Pascal back when they were teaching me to program (and I still have both my copies of Oh! Pascal! on my shelves- good books).

-- http://enfranchisedmind.com/blog/posts/my-question-on-go/#comment-37040

---

notes on javascript design:

http://yuiblog.com/crockford/

---

" that static type systems are often "wrong"...the type system is "wrong" whenever it cannot match the intended computational model.

Researchers love to go on about whether a type system is "sound" or not, and "unsound" type systems are considered bad. C++ and Java have "unsound" type systems. What researchers fail to realize is that until they can come up with a type system that is never "wrong" in the sense I described earlier, they will continue to frustrate their users, and their languages will be abandoned for more flexible ones. (And, Scala folks, it can't just be possible to express things like property lists it has to be trivial.) " -- http://steve-yegge.blogspot.com/2008/02/portrait-of-n00b.html

(see also http://www.scala-lang.org/node/29 )

and even when the type system is sufficiently expressive and simple it can make things take longer if you have to change a bunch of types whenever you do anything:

" Static type models have weight and inertia. They take time to create, time to maintain, time to change, and time to work around when they're wrong. They're just comments, nothing more. All metadata is equivalent in the sense of being tangential documentation. And static type models get directly in the way of flexibility, rapid development, and system-extensibility. " -- http://steve-yegge.blogspot.com/2008/02/portrait-of-n00b.html

---

" Pushing it even further

In addition to how centrally you want to use the Properties pattern in your system, you can also decide how recursive to make it: do your properties have explicit meta-properties? Do you have metaprogramming hooks? How much built-in reflection do you offer?

JavaScript? offers a few metaprogramming hooks. One such hook is the recently-introduced __noSuchMethod__, which lets you intercept a failed attempt to invoke a nonexistent function-valued property on an object.

Unfortunately JavaScript? does not offer as many hooks as I'd like. For instance, there is no corresponding __noSuchField__ hook, which limits the overall flexibility somewhat. And there are no standard mechanisms for property-change event notification, nor any reasonable way to provide such a mechanism. So JavaScript? gets it mostly right, but it stops short, possibly for performance reasons, of offering a fully-extensible metaprogramming system such as those offered by SmallTalk? and to some extent, Ruby. "

---

on javascript by it's inventor: " I'm not proud, but I'm happy that I chose Scheme-ish first-class functions and Self-ish (albeit singular) prototypes as the main ingredients. The Java influences, especially y2k Date bugs but also the primitive vs. object distinction (e.g., string vs. String), were unfortunate. "

commentary frm others: " Unmet expectations are tough to deal with. When you expect a static, enterprise-y language like the Java language but end up with a language that has Java code-like syntax but behaves more like Scheme and Self you're justifiably surprised. If you like dynamic languages, this would be a welcomed surprise; if you don't, or they're just unfamiliar to you, then programming in JavaScript? might be unpleasant.

JavaScript? also has some genuine warts: forced global variables, scoping issues, semicolon insertion, the inconsistent behavior of ==, and more. For these issues, JavaScript? programmers have developed an array of patterns and best practices to aid the development of reliable software. The next section discusses a few patterns to use, and some to avoid, to make the best use of JavaScript?'s prototypal object system. " -- http://www.ibm.com/developerworks/library/wa-protoop/index.html

---

Steve Yegge talks of XML as having a sort of optional static typechecking through DTDs, and a more expressive variant via XML Schema and Relax NG schema.

i guess one way to typecheck is to begin by putting inserting a typecheck in the code before each access to a variable (like a dynamic language would do). Then if you want to make it more static, just extend the scope of that whenever you can determine what type the typecheck would demand at compile time; move all the typechecks for one function to the beginning of that function and remove dups; now you are requiring that the variables never change type within the scope of one function. Now you can promote them out of the function (for the args) to get a compile-time view of some of the function args.


a superclass is really just presenting an API to programmers who would subclass it.


in https://sites.google.com/site/steveyegge2/scheming-is-believing , section Designing for growth, Yegge says a good language does the following:

adaptable: " What makes a language adaptable? Well, languages are how you say things, so if you need to start expressing new concepts, your language needs to be able to grow to express them. In particular, you have to be able to express them cleanly, without having to jump through strange hoops, mix in a bunch of historical gunk, and write tons of comments that say: "warning: severe hack ahead". So that rules out Perl and C++. "

well-specified

" It's easiest to make a solid language specification if the language is small, orthogonal, and consistent. "

" In addition to relative simplicity (at least syntactic simplicity) and a thorough spec, another key element of adaptability is that the language needs to be extensible...

C provides a simple but reasonably effective extension mechanism via its preprocessor macros. These proved very useful for a wide variety of problems not addressed by the language, such as feature-conditional compilation. In the long term, though, it doesn't scale very well, being neither very expressive nor very well-integrated with the language and tools.

But at least C has a macro system. Java has absolutely nothing in this space admittedly the C preprocessor wasn't something you'd want to copy, and C++ templates are just about the ugliest thing ever invented by the hand of man, but that doesn't mean you should leave the problem entirely unsolved. "

friendly user community

"

I could list other factors that contribute to language scalability: the complexity of the syntax, for instance, is a real barrier to scaling, for many reasons. You really don't want a complicated syntax; it'll come back to bite you. Perl, C++ and Java all have artificially complicated syntax: too much for too little semantic benefit. Java is the least bitten of the three, but it still inherits a lot of cruft from C++, which was designed by a rank amateur. (As was Perl.)

The module system is another big one. Runtime introspection support is another. In fact, all of the choices made in designing a language, whether explicit or inadvertent, have some impact on how far the language will scale before your system becomes too complex for human beings to keep evolving it.

But all of these contributors pale in comparison to extensibility i.e., the extent to which users can customize or change the behavior of the language itself to suit their needs.

And all languages, bar none, pale in comparison to Lisp when it comes to extensibility (or adaptability, as I was calling this property earlier). It doesn't matter how cool or sophisticated or far-reaching you think your favorite language's extension mechanisms are: compared to Lisp, all other languages are orders of magnitude less capable of evolution. "

---

" One obvious problem is that Java's type extension mechanism is limited to defining classes and interfaces. You can't create new primitive types, and there are severe limitations on what you can do with classes and interfaces. As a random example, you can't subclass a static method a feature that would occasionally be extremely useful, and which is present in other languages. And a class can't get its own name at compile-time, e.g. to pass to a static logger. (Jeez.)

One that really irks me: there's no such thing as a "static interface" an interface full of static methods that a Class object promises to implement. So you have to use reflection to look up factory methods, hardwire class names in your code, and so on. The whole situation is actually quite awful.

As a result of Java's whimsical limitations, you often find objects or situations in the real world that are very difficult to express in Java's type system (or C++'s, for that matter, on which Java's is modeled). Many Design Patterns are in fact elaborate mechanisms for working around very serious limitations of the C++ and Java type systems.

An even more subtle point is that every single element of a programming language has a type, even though the language doesn't actually recognize them as distinct entities, let alone typed ones.

Here's an example of what I mean by being able to tag things until you're blue in the face: Take a for-loop. It's got a type, of course: for-loop is a type of loop. It can also be viewed as a type of language construct that introduces a new scope. And it's a type that can fork program control-flow, and also a type that's introduced by a keyword and has at least two auxiliary keywords (break and continue).

You could even think of a for-loop as a type that has lots of types, as opposed to a construct like the break statement, which doesn't exhibit as much interesting and potentially type-worthy behavior. A type is just a description of something's properties and/or behavior, so you can really get carried away with overengineering your type declarations. In extreme cases, you can wind up with a separate Java interface for nearly every different method on a class. It's pretty clear why Python and Ruby are moving towards "duck typing", where you simply ask an object at runtime whether it responds to a particular message. If the object says yes, then voila it's the right type. Case closed.

Ironically, even though every single Java language construct has many (possible) types, Java itself is oblivious to them. Even Java's runtime reflection system is only capable of expressing the types provided by the OO mechanism (plus some stragglers like the primitive types). Reflection has no visibility inside of methods. If you want to write Java code that reads or writes Java code, then you have to come up with your own object model first, and potentially wrestle with complex parser generators and so on.

 Many languages that don't offer you much in the way of static type annotations (e.g. Ruby, Python, Perl) still have tremendously rich type systems. They provide you with more flexibility in defining your types, using far less code, and still providing the same level of safety and automatic documentation. Declaring a bunch of types doesn't make your code safe, and not declaring them doesn't make it unsafe. Types are just helpers, and making the right type choices is a fuzzy art, one that boils down to taste. 

..

" -- https://sites.google.com/site/steveyegge2/scheming-is-believing

"

Another example: you can't write logging, tracing, or debugging statements intelligently in Java. For instance, if you want to write something like this:

 debug("An error occurred with this object: " + obj);

where "debug" is a function that checks whether a debugging flag is set, and if so, prints the message somewhere.

If you do this, someone will happily point out that in Java, function arguments are evaluated before calling the function, so even if debugging is turned off, you're doing a bunch of work with that string concatenation: creating a StringBuffer? object, copying in the string literal, calling a polymorphic toString() function on obj, creating a new string to hold the object's description, which possibly involves more StringBuffers? and concatenation, then returning the StringBuffer? on the stack, where its contents are appended to the first StringBuffer?, possibly resulting in a reallocation of the memory to make room, and then you're passing that argument on the stack to the debug() function, only to find that debugging is off.

Oops.

And that's the best-case scenario. In the worst case, you could trigger a thread context switch, or a garbage collection, or an unneeded operating system page-cache fetch, or any number of other things that you really don't want happening in production because of that debug line, at least when debugging is off.

 There's no way to fix this. Even a preprocessor couldn't do it for you. The standard hacky workaround is to write something like this instead:
 if (DEBUG) debug("An error occurred with this object: " + obj);

where "DEBUG" is a constant in your code somewhere. This approach is fraught with problems. The debug flag may need to be shared across multiple classes, so you need to either declare it in each class, or export it from one of them. The name of the flag appears right there, inlined with your code, and there's no way to avoid typing it in hundreds of places (unless you use AspectJ?). And it's just plain ugly.

"

" For instance, in Java you must double-escape all the metacharacters in your regular expressions, not to mention create Pattern and Matcher objects, all because you can't make any changes to Java's syntax. ... Another example: you can't write logging, tracing, or debugging statements intelligently in Java. For instance, if you want to write something like this:

 debug("An error occurred with this object: " + obj);

where "debug" is a function that checks whether a debugging flag is set, and if so, prints the message somewhere. If you do this, someone will happily point out that in Java, function arguments are evaluated before calling the function, so even if debugging is turned off, you're doing a bunch of work with that string concatenation ... There's no way to fix this. Even a preprocessor couldn't do it for you. .... This approach is fraught with problems. The debug flag may need to be shared across multiple classes, so you need to either declare it in each class, or export it from one of them. The name of the flag appears right there, inlined with your code, and there's no way to avoid typing it in hundreds of places (unless you use AspectJ?). And it's just plain ugly.

My third and final Java example: sometimes you really do need multiple inheritance. ...

 The regexp-escaping problem is a lexical problem: an eval-time or compile-time macro system won't help you, because the lexical analyzer has already done its dirty work before the parser ever sees the string. If you wanted to provide a way around this in Java, without using a preprocessor (which is a hack), you'd need an API that allows you to interact with the lexer. That's all. Just an API, and a way to arrange to invoke it before the rest of your code is lexed.

The debug-flag problem is an evaluation-time problem. You can fix problems like this either by adding support for lazy evaluation to your language, or by adding a macro system. They amount to mostly the same thing, except a macro system lets you add some syntactic sugar as well, which is sometimes appropriate.

The multiple-inheritance/delegation problem is a problem with the interpreter semantics not being flexible enough. It manifests later than eval-time, and it's conceivable that you could fix it without needing to change the language syntax. For instance, if Java simply had a methodMissing method in java.lang.Object, one that was called every time someone tried to invoke a nonexistent method on you, then you could very easily implement your own delegation strategy. It would be far easier to code, far more resilient to interface changes, and it would even allow you to abstract your delegation policy into another class, so you could share it with other classes.

 Because no syntax changes are needed, the third problem illustrates a class of problems that can be solved using metaprogramming, which lets you change the built-in behavior of classes, e.g. by adding methods or properties, overriding built-in methods, and so on.

Three problem classes, three different techniques: Lexer (or "Reader") macros, evaluator macros, and metaprogramming.

C++ lets you do a little of all three of these things with its Template system, and with its operator overloading, which is a limited (but often useful) form of metaprogramming. Not enough with any of them, sadly, and it's too hard to implement what little flexibility it allows you. But it's much better than C's preprocessor, and it's a thousand times better than Java's big fat nothing. It of course would be infinitely better, being a divide-by-zero error, except that we'll give Java some credit for at least not copying C++'s broken templates.

Ruby and Python offer fairly flexible metaprogramming models, but no macros or reader-macros. So they're both susceptible to the first two kinds of problem I mentioned.

Perl has... I dunno. Something. A whole mess of somethings. I know they don't have macros, since they were discussing adding them on the Perl newsgroups a year ago. Perl has some metaprogramming features, but they're relatively limited in scope. And I don't think it has reader macros, although it may offer some sort of preprocessor.

I hope I've demonstrated that reader macros, compiler macros and metaprogramming really can make your code a lot smaller, a lot faster, and a lot more robust. Like any other language feature, you can abuse them horribly. Unlike other language features, however, you can use macros and metaprogramming to fix broken language features, and in fact make the other features less easily abused. "

---

from http://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem

Python has the same structure as Perl, but unlike Perl includes it in the syntax of the language. The order of inheritance affects the class semantics. Python had to deal with this upon the introduction of new-style classes, all of which have a common ancestor, object. Python creates a list of a classes using the C3 linearization algorithm. That algorithm enforces two constraints: children precede their parents and if a class inherits from multiple classes, they are kept in the order specified in the tuple of base classes. Thus, the method resolution order is: D, B, C, A.[4][5]

Scala allows multiple instantiation of traits, which allows for multiple inheritance by adding a distinction between the class hierarchy and the trait hierarchy. A class can only inherit from a single class, but can mix-in as many traits as desired. Scala resolves method names using a right-first depth-first search of extended 'traits', before eliminating all but the last occurrence of each module in the resulting list. So, the resolution order is: [D, C, A, B, A], which reduces down to [D, C, B, A].

---

" One obvious problem is that Java's type extension mechanism is limited to defining classes and interfaces. You can't create new primitive types, and there are severe limitations on what you can do with classes and interfaces. As a random example, you can't subclass a static method a feature that would occasionally be extremely useful, and which is present in other languages. And a class can't get its own name at compile-time, e.g. to pass to a static logger. (Jeez.)

One that really irks me: there's no such thing as a "static interface" an interface full of static methods that a Class object promises to implement. So you have to use reflection to look up factory methods, hardwire class names in your code, and so on. The whole situation is actually quite awful. "

---

" In stark contrast with every other language out there, Lisp only has two syntactic forms, atoms (i.e. symbols, numbers, strings) and the s-expression (which stands for "symbol expression"), which looks like this:

(I am an s-expression.)

They can be nested to arbitrary depth:

(I am an s-expression. (I am a sub-expression. (We make a tree, actually.) (Pleased to meet you!)))

OK, that's Lisp's syntax. Most versions of Lisp add in a small handful of shortcuts, as long as they don't change the overall tree-structure of the code.

And Lisp's runtime machine only needs to support a handful of things:

    anonymous functions, also called "lambda" expressions
    named variables
    nested scopes, one per lambda expression
    a single control-flow construct called a "continuation"
    some kind of I/O

"


" Perl can't do lists because Larry made the tragically stupid decision early on to flatten them automatically. So (1, 2, (3, 4)) magically becomes (1, 2, 3, 4). Not that you ever want it to work this way. But Larry happened to be working on some problem for which it was convenient on that particular day, and Perl's data structures have been pure exploded whale ever since. "

" For the most part, Ruby took Perl's string processing and Unix integration as-is, meaning the syntax is identical, and so right there, before anything else happens, you already have the Best of Perl. And that's a great start, especially if you don't take the Rest of Perl.

But then Matz took the best of list processing from Lisp, and the best of OO from Smalltalk and other languages, and the best of iterators from CLU, and pretty much the best of everything from everyone. "

" Python's author, Guido Van Rossum, also made some boneheaded technical blunders early on none quite as extravagant as Larry's blunders, but a few were real doozies nonetheless. For instance, Python originally had no lexical scoping. But it didn't have dynamic scoping either, and dynamic scoping may have its share of problems, but it at least sort of works. Python had NOTHING except for global and local (function) scope, so even though it had a "real" OO system, classes couldn't even access their own damned instance variables. You have to pass a "self" parameter to EVERY instance method and then get to your instance data by accessing it through self. So everything in Python is self, selfself, selfselfself, selfSELFselfSELF__SELF__, and it drives you frigging nuts, even if you don't mind the whitespace thing. "

---

http://bartoszmilewski.com/2009/02/26/message-passing-atoms-mvars/

---

http://fpcomplete.com/asynchronous-api-in-c-and-the-continuation-monad/