A left-to-right syntax programming language?

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

User avatar
davedrowsy
Posts: 26
Joined: Wed Jan 16, 2013 5:27 pm UTC
Location: Durham, NC

A left-to-right syntax programming language?

Postby davedrowsy » Wed Jul 03, 2013 8:46 pm UTC

So I've been working on an idea for a new programming language that uses a strict left-to-right syntax pattern built around the idea of piping data from "left to right." I got the idea when I first came across "piping" (|>) in F#, and I wondered if one couldn't do everything this way. I find it so readable, I wonder if it could enhance productivity if you expanded upon that idea and turned it into the foundation of a new language. I'm curious to know if anyone is familiar with any other programming languages that use this type of syntax (at least a little bit).

To give you a better idea of what I'm envisioning, my language would make liberal use of the "|" character as a pipe operator, which would work much like it's used in bash. Almost every line of code would start with one or more pieces of data, which is then piped from left to right through one or more functions to obtain a result, which you would then (at the end of the line) assign to a variable, print, or do some other I/O action with it.

Code: Select all

3, 4 | + => x
# in most languages this would be written as "x = 3 + 4"

5 => x
# variable assignment

3, 4 | + | *3 | print
# equivalent to "print (3 + 4) * 3"
# à la Haskell, (*3) is used as a partially applied function that takes
# one argument and multiplies it by 3

"hello world" | capitalize | reverse | print
# prints "dlrow olleH"


In thinking about this, I've encountered a number of interesting syntax problems, some of which I'm still toying with. For instance, what if you have one piece of data that you want to pipe to a function that takes two arguments? I came up with the idea of using the comma as an operator to do "branching" within one line of code. I used it above in the "3, 4 | +" example. The line starts as two "branches," one containing 3 and the other containing 4, which are piped to the function "+", which takes 2 arguments so it uses both 3 and 4. I've even developed a syntax for working on multiple branches separately, all in the same line of code! It's surprisingly readable.

I haven't actually tried creating a parser for this conceptual language yet, but I've done a lot of work in figuring out how the syntax might work, and in doing so I've found that it's quite easy to express things more concisely using this kind of "everything is left-applicative" syntax, and the code reads more intuitively (at least to my eyes). You can accomplish a lot more tasks in a single line of code, whereas in other C-like languages you would have to store data intermediately in "throw-away variables" and keep working on said data on the next line. I'm surprised that I haven't heard of any other languages out there that do this kind of thing. Have any of you guys?

User avatar
davedrowsy
Posts: 26
Joined: Wed Jan 16, 2013 5:27 pm UTC
Location: Durham, NC

Re: A left-to-right syntax programming language?

Postby davedrowsy » Wed Jul 03, 2013 9:02 pm UTC

By the way, I did notice that Ruby gets halfway there with its method chaining. In fact, my last example could be expressed in Ruby similarly as:

Code: Select all

print "hello world".capitalize.reverse


My idea would be to take this a step further and stick the "print" function call at the end, since that's what you're passing everything to. Every line of code starts with data and ends with an IO action of some sort.

User avatar
hotaru
Posts: 1042
Joined: Fri Apr 13, 2007 6:54 pm UTC

Re: A left-to-right syntax programming language?

Postby hotaru » Wed Jul 03, 2013 9:41 pm UTC

you mean something like factor?

Code: Select all

IN: scratchpad 3 4 + 3 * .
21
IN: scratchpad "hello world" capitalize reverse .
"dlrow olleH"

Code: Select all

factorial product enumFromTo 1
isPrime n 
factorial (1) `mod== 1

User avatar
thoughtfully
Posts: 2253
Joined: Thu Nov 01, 2007 12:25 am UTC
Location: Minneapolis, MN
Contact:

Re: A left-to-right syntax programming language?

Postby thoughtfully » Wed Jul 03, 2013 10:07 pm UTC

Yeah, I think he's describing a lot of stack based / concatenative languages. You can also have postfix notation in other languages, like in the Ruby example, which is typical of the object-oriented style. Anyone here still using RPN calculators? I have an app on my phone; I gave up on the HP48 when I stopped taking physics classes.
http://concatenative.org/

I once wrote a Python module to aid writing in this style. I found it too awkward to be worth the improved readability. It can also get inefficient very quickly. It could stand being refactored to use generators where approprate, but I abandoned it. I see I even included comments with examples. Lucky you :)

Code: Select all

def fbuildcls(klass):
   # klass != self.__class__ !
   class fcls(klass):
      # reduce_apply, n=0
      def __mul__(self, f):
         if type(f) == type(0):
            return self.__class__(self.__class__.__base__.__mul__(self, f))
         else:
            return self.__class__([ f(i) for i in self ])
      def __rmul__(self, f):
         if type(f) == type(0):
            return self.__class__(self.__class__.__base__.__rmul__(self, f))
         else:
            return self.__class__([ f(i) for i in self ])
      def __pow__(self, f):
         return self.__class__([ f(*i) for i in self ])
      # reduce_apply, n=1
      def __mod__(self, f):
         #return self*(lambda x: self.__class__([ f(i) for i in x ]))
         return self.__class__([ self.__class__( [f(i) for i in j]) for j in self])
      def __add__(self, o):
         return self.__class__(self.__class__.__base__.__add__(self,o))
      def __radd__(self, o):
         return self.__class__(self.__class__.__base__.__radd__(self,o))
      def join(self, delim):
         return delim.join(self)

   return fcls

flist  = fbuildcls( list)
ftuple = fbuildcls(tuple)


def reduce_apply(fl, f, n):
   if type(f) == type({}):
      if n in f:
         if n == 0:
            return f.__class__([ f[n](i) for i in fl ])
         else:
            print '%d: %s' % (n,[f[n](z) for z in fl])
            return f.__class__([ reduce_apply(y, f, n-1) for y in [f[n](z) for z in fl] ])

   if n == 0:
      return f.__class__([ f(i) for i in fl ])
   else:
      return f.__class__([ reduce_apply(y, f, n-1) for y in fl ])


# from python cookbook recipe 52549 (comments)
def rcurry(*args, **kwargs):
    function, args = args[0], args[1:]
    def result(*rest, **kwrest):
        combined = kwargs.copy()
        combined.update(kwrest)
        return function(*rest + args, **combined)
    return result

def lcurry(*args, **kwargs):
    function, args = args[0], args[1:]
    def result(*rest, **kwrest):
        combined = kwargs.copy()
        combined.update(kwrest)
        return function(*args + rest, **combined)
    return result


# turn a function which returns a list into a function that returns an flist
def asflist(f):
   return lambda *x: flist(f(*x))


# >>> from string import split, join
# >>> flist('a,b,c;x,y,z'.split(';'))*rcurry(split,',')*rcurry(join,',').join(';')
# 'a,b,c;x,y,z'

# >>> flist('1,2,3;-1,-2,-3'.split(';'))*rcurry(split,',')%float
# [[1.0, 2.0, 3.0], [-1.0, -2.0, -3.0]]


# >>> fsplit=asflist(split)
# >>> a=flist('1:2,3:4;5:6,7:8'.split(';'))*rcurry(fsplit,',')%rcurry(fsplit,':')
# >>> a
# [[['1', '2'], ['3', '4']], [['5', '6'], ['7', '8']]]

# >>> reduce_apply(['1','2','3'], float, 0)
# [1.0, 2.0, 3.0]

# >>> reduce_apply([['1','2'],['3','4']], float, 1)
# [[1.0, 2.0], [3.0, 4.0]]

# >>> reduce_apply(a, float, 2)
# [[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]]

# order of reduction is reduced due to the *'s reduction
# >>> flist('1:2,3:4;5:6,7:8'.split(';'))*rcurry(fsplit,',')%rcurry(fsplit,':')*rcurry(reduce_apply, float, 1)
# [[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]]

# order of reduction is reduced due to the %'s reduction
# >>> flist('1:2,3:4;5:6,7:8'.split(';'))*rcurry(fsplit,',')%rcurry(fsplit,':')%rcurry(reduce_apply, float, 0)
# [[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]]

# very inefficient syntactic sugar. the list is broken down and rebuilt for every application of reduce_apply.

# compare to:
"""
l = []
for i in s.split(';'):
   m = []

   for j in i.split(','):
      n = []

      for j in j.split(':'):
         n.append(float(k))

      m.append(n)

   l.append(m)

or:
l = [ [ [ float(k) for k in j.split(':') ] for j in i.split(',') ] for i in s.split(';') ]
"""
# still, easier on the eyes, if symbols are defined for all the steps:
# >>> mkdim2 = rcurry(fsplit,',')
# >>> mkdim3 = rcurry(fsplit,':')
# >>> tofloat = rcurry(reduce_apply, float, 0)

# >>> dim0 = flist('1:2,3:4;5:6,7:8'.split(';'))

# >>> dim1*mkdim2%mkdim3%tofloat
# [[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]]

# nice!

# drumroll...
# >>> d = { 2:mkdim2, 1:mkdim3, 0:float }

# reduce_apply(dim1,d,2)
# [[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]]

# >>> tofloat = rcurry(reduce_apply, d, 1)
# >>> dim1*mkdim2*tofloat
# [[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]]

See also:
http://evincarofautumn.blogspot.com/201 ... tters.html
Image
Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
-- Antoine de Saint-Exupery

User avatar
davedrowsy
Posts: 26
Joined: Wed Jan 16, 2013 5:27 pm UTC
Location: Durham, NC

Re: A left-to-right syntax programming language?

Postby davedrowsy » Thu Jul 04, 2013 12:49 am UTC

Ah! Factor's syntax is actually really close to my would-be-language's syntax. Thanks for sharing, both of you. I've never heard of concatenative languages, but they look like exactly what I had in mind. The stack is a great connection that I didn't think of. Very interesting stuff! Gonna look more into Factor now and see what features I can rip off. :)

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: A left-to-right syntax programming language?

Postby troyp » Thu Jul 04, 2013 6:23 am UTC

Forth is another famous stack-based language.

You might also be interested in APL, where evaluation is strictly right-to-left, and Dataflow Programming which is based on piping data through operations.

As for other languages with a "pipe" operator...well, you could define it in any Lisp or ML/Haskell dialect, using macros in the former and user-defined operators in the latter.
Haskell doesn't have such an operator built in. You'd usually write things in the opposite direction using $, (or using concatenation in point-free style). You could define |> in Haskell with:

Code: Select all

(|>) = flip ($)
Haskell does have a left-to-right operator for monadic bind, though (>=).

Clojure has "threading macros" that take an expression and pipe it through a series of functions as either first or last argument:

Code: Select all

(-> 5
    (- 1)    ;; (- 5 1) gives 4
    (* 2))   ;; (* 4 2) gives 8
(->> 5
     (- 1)   ;; (- 1 5) gives -4
     (* 2))  ;; (* 2 -4) gives -8


OO languages can also express data piping using "method chaining", although it's only idiomatic (and possible with built-in classes) in certain languages, eg. Javascript:

Code: Select all

[1,2,3].reverse().concat([4,5])  // [3,2,1,4,5]

User avatar
davedrowsy
Posts: 26
Joined: Wed Jan 16, 2013 5:27 pm UTC
Location: Durham, NC

Re: A left-to-right syntax programming language?

Postby davedrowsy » Thu Jul 04, 2013 5:52 pm UTC

I knew there had to be a way to do this in Haskell! I just hadn't dug deep enough. I bet I could implement this kind of thing using a combination of "flip ($)" and the monadic bind operator >=.

That Clojure syntax looks awesome, too! I've always wanted to learn Clojure. Maybe I'll just do that instead of trying to reinvent the wheel by creating a new language.

User avatar
PM 2Ring
Posts: 3652
Joined: Mon Jan 26, 2009 3:19 pm UTC
Location: Mid north coast, NSW, Australia

Re: A left-to-right syntax programming language?

Postby PM 2Ring » Fri Jul 05, 2013 4:44 am UTC

thoughtfully wrote:Anyone here still using RPN calculators?

No, but I did write a simple RPN calculator in Python a few years ago.

Code: Select all

#!/usr/bin/env python

''' Simple RPN calculator '''

import readline, shlex

class myline:
    ''' Read from Standard Input, with line editing '''
    s = ""
    def read(self, n):
        ''' Get the next n chars '''
        while n > len(self.s):
            try:
                b = raw_input()
            except EOFError:
                return ""
            self.s += b + "\n"
        a, self.s = self.s[:n], self.s[n:]
        return a

    def readline(self):
        ''' Get the next line '''
        if 0 == len(self.s):
            try:
                b = raw_input()
            except EOFError:
                return ""
            self.s += b + "\n"
        a, self.s = self.s, ""
        return a


def main():
    ''' Simple RPN calculator '''
    stack = []

    #Set up lexer to split on words, numbers & operators
    lex = shlex.shlex(myline())
    lex.whitespace_split = False
    alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    lex.wordchars = alpha + alpha.lower() + "0123456789_."

    #Predefined operators
    def add(): a = stack.pop(); stack[-1] += a
    def sub(): a = stack.pop(); stack[-1] -= a
    def mul(): a = stack.pop(); stack[-1] *= a
    def div(): a = stack.pop(); stack[-1] /= a
    def mod(): a = stack.pop(); stack[-1] %= a
    def pwr(): a = stack.pop(); stack[-1] **= a

    def pop(): stack.pop()
    def dup(): stack.append(stack[-1])
    def exch(): stack[-1], stack[-2] = tuple(stack[-2:])
    def dump(): print stack

    ops = {
        "+": add, "-": sub, "*": mul, "/": div, "%": mod, "^": pwr,
        "pop": pop, "dup": dup, "exch": exch,
        #"=": dump
    }

    #Loop to process commands
    for tt in lex:
        #print "Token: " + repr(tt)
        if tt == "=":
            print stack
            #stack = []
        elif tt in ops:
            try:
                ops[tt]()
                print stack[-1]
            except IndexError:
                print "Beep"
        else:
            stack.append(eval(tt))

    print "Stack:",
    print stack

if __name__ == '__main__':
    main()


I haven't used Forth for decades, but I do like programming in PostScript.

User avatar
scarecrovv
It's pronounced 'double u'
Posts: 674
Joined: Wed Jul 30, 2008 4:09 pm UTC
Location: California

Re: A left-to-right syntax programming language?

Postby scarecrovv » Sat Jul 06, 2013 1:48 pm UTC

troyp wrote:Haskell doesn't have such an operator built in. You'd usually write things in the opposite direction using $, (or using concatenation in point-free style). You could define |> in Haskell with:

Code: Select all

(|>) = flip ($)
Haskell does have a left-to-right operator for monadic bind, though (>=).

Bind is actually ">>=". Honestly though, I never use ">>=", I use reverse bind, "=<<", because it works in the same direction as "$" and ".".

alessandro95
Posts: 109
Joined: Wed Apr 24, 2013 1:33 am UTC

Re: A left-to-right syntax programming language?

Postby alessandro95 » Sat Jul 06, 2013 9:53 pm UTC

Since APL was suggested I cannot avoid suggesting J as well, which share a lot with APL (startingfrom the developer), including the right to left evaluation, it isn't exactly what you were searching for but better than nothing
The primary reason Bourbaki stopped writing books was the realization that Lang was one single person.

User avatar
davedrowsy
Posts: 26
Joined: Wed Jan 16, 2013 5:27 pm UTC
Location: Durham, NC

Re: A left-to-right syntax programming language?

Postby davedrowsy » Sun Jul 07, 2013 9:49 pm UTC

I'm becoming really drawn to Clojure's thread macro (->). It's a nice way to embed that whole concept into a larger program that you would write more imperatively, if that makes sense. It seems like the concatenative approach is great way to write a lot of data manipulation / transformation routines, but there are some things that are just easier to understand / more readable in the "traditional," imperative paradigm.

Derek
Posts: 2179
Joined: Wed Aug 18, 2010 4:15 am UTC

Re: A left-to-right syntax programming language?

Postby Derek » Tue Jul 09, 2013 12:32 am UTC

AviSynth is an imperative scripting language for editing videos that allows (and even encourages) a pseudo-concatenative style by implicitly assigning and passing a "last" variable if no explicit value is provided. To use an example from their wiki:


Code: Select all

Version()
ReduceBy2
Trim(120,150)
FadeOut(10)

# Is equivalent to:

last = Version()
last = ReduceBy2(last)
last = Trim(last, 120, 150)
last = FadeOut(last, 10)


I believe such idioms are occasionally possible in Perl as well, due to $_ and related variables, but I'm not a Perl expert so I don't know the full extent of it (and I don't want to).

sweeneyrod
Posts: 8
Joined: Sat Nov 02, 2013 5:39 pm UTC

Re: A left-to-right syntax programming language?

Postby sweeneyrod » Sat Nov 02, 2013 7:06 pm UTC

Some short Python code I created for this purpose:

Code: Select all

class F:
    def __init__(self, function, *arguments, index=0):
        self.function = function
        self.arguments = arguments
        self.functions = [(function, arguments, index)]
           
    def __or__(self, other):
        new = F(None, None)
        new.functions = self.functions + other.functions
        new.function, new.arguments, new.index = new.functions[0]
        return new

    def __rrshift__(self, other):
        value = other
        for function, arguments, index in self.functions:
            args = list(arguments)
            args.insert(index, value)
            value = function(*args)
        return value
           
   
    def __repr__(self):
        return "F({}, *{}, index={})".format(self.function, self.arguments, self.functions[0][2])

if __name__ == "__main__":
    from operator import *
    x = 5
    y = x >> (F(add, 2) | F(sub, 4) | F(mul, 8) | F(floordiv, 16, index=1))
    print(y)
    x >>= (F(lambda x, y, z: x * y**z, 1, 2) | F(lambda x, y: str(x) + y + "!", "!!!!!"))
    print(x)

kchaloux
Posts: 2
Joined: Thu Jan 09, 2014 1:57 pm UTC

Re: A left-to-right syntax programming language?

Postby kchaloux » Thu Jan 09, 2014 2:08 pm UTC

Late response is late! Thought I'd chime in, a friend of mine is developing a concatenative functional programming language called Kitten. It has a syntax similar to the proposed one here, albeit a bit lighter weight. It's heavily inspired by Haskell and Factor.

Code: Select all

1 2 + sayInt                     // prints "3" to the console
[1, 2, 3, 4, 5] 0 {+} foldl      // performs a left-leaning fold and pushes the result (15) onto the stack


It's on Github at the moment. You need Haskell to build it, and it's still in the early stages of development, but it works well enough to write a program in.
It also has a website at kittenlang.org, with links to github and code examples.

(Apparently I'm not allowed to post actual links, lest I be flagged as spam)

User avatar
davedrowsy
Posts: 26
Joined: Wed Jan 16, 2013 5:27 pm UTC
Location: Durham, NC

Re: A left-to-right syntax programming language?

Postby davedrowsy » Thu Jan 09, 2014 6:26 pm UTC

Whoa! It's weird that you posted this just now... By some freak coincidence, I was just thinking about this idea again a few minutes ago!

Thanks for posting. Kitten is intriguing -- I'll have to check it out.

In the 6 months since I posted this thread, I've been doing a lot of exploration and introspection about the idea of creating a new concatenating language like this. I read Seven Languages in Seven Weeks and was especially intrigued by Io, which seems to have almost exactly the kind of syntax I'm envisioning. I found it really fun to play around with Io while going through that chapter in 7LI7W, and it makes me sad that no real development has happened with Io in some years. I'd probably make it one of my main languages if it were still being actively developed. There's another language called Ioke that is heavily inspired by Io (along with Ruby, Smalltalk, and Lisps) and has a very similar syntax, but it's still in its infancy and doesn't seem very mature at this stage. Hopefully in time it will develop nicely -- from the examples, the syntax seems quite nice.

I've also been learning a great deal of Clojure, and there are a lot of times where I gravitate toward the threading macros -> and ->>, and using them tends to make my code quite readable. For example, the code snippets I used in my OP could be written like this in Clojure using the threading macros:

Code: Select all

(->> [3 4] (apply +) (print))

(->> [3 4] (apply +) (* 3) (print))

(require '[clojure.string :as s])
(->> "hello world" (s/capitalize) (s/reverse) (print))


That's pretty damn nice, but I'm still interested in the idea of a concatenating language that has a more readable punctuation style that doesn't require you to, for example, put parentheses around every function. From that last post, it looks like Kitten's approach is pleasantly simple. I'll have to check it out when I have more time!

User avatar
davedrowsy
Posts: 26
Joined: Wed Jan 16, 2013 5:27 pm UTC
Location: Durham, NC

Re: A left-to-right syntax programming language?

Postby davedrowsy » Fri Jan 10, 2014 9:19 pm UTC

I just checked out the introduction and tutorial on the Kitten site -- very nice! I'm interested to see how it develops. I love how functions are first class citizens, to the point that you can define anonymous functions on the spot, like {* 2}, just by putting code between curly braces. So cool!

Reading through the tutorial, I realized that the theoretical language I'm envisioning isn't quite the same thing. It would also be concatenating, like Kitten, but would not be stack-based. In Kitten, (if I'm understanding correctly), you would add 3 numbers together like this:

Code: Select all

1 2 3 + +


So the progression of your stack goes:
1
1 2
1 2 3
1 (2 3 +)
1 5
(1 5 +)
6

In my theoretical language, it would be more like this:

Code: Select all

1, 2, 3; +


You have kind of a "horizontal stack" (if you can call it that) defined all at once (1, 2, 3), and you're passing that to the "+" function, which can add 2 or more numbers together (like in Clojure and other Lisps). You would start from one "segment," which can be one or more literals, and then pass everything in the segment on to the next segment containing a function, the function is applied to the data, and whatever is returned goes onto the next segment, etc. I wonder if maybe I'm just arguing semantics, though, and what I'm talking about is actually just a different way of describing a stack-based language :?

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: A left-to-right syntax programming language?

Postby troyp » Sun Jan 12, 2014 5:45 am UTC

Most concatenative languages have mechanisms to allow you to define containers/collections of arguments so you can effectively have varargs operations even though the arity of operations is fixed.

Code: Select all

{ 1 2 3 } +
(If you had a distinction between values and operations, you could do with only the initial delimiter.)

Your syntax doesn't really look concatenative, although I guess it could be. You could interpret "," as an operation that does:

Code: Select all

x = pop item
if top item is a ",-collection":
    xs = pop item
    push (",-collection" formed by appending x to xs)
else:
    push (",-collection" containing x only)
note: This pseudocode is assuming a stack interpretation for simplicity.

I think a syntax using delimiters would be much more natural (assuming you're still using a strict left->right evaluation)

I guess the question is: are all your operations variadic ones that use this syntax (or equivalently, monadic ones taking a collection) or are these operations mixed with "standard" operations that operate on a fixed number of arguments. If the latter, I'd say you still have a concatenative language. If the former, it looks more like an applicative language (specifically: a postscript lisp that uses an argument list syntax in place of parentheses).

edit: I forgot to say, one serious limitation of that argument list syntax is that it can't handle nesting. So you'd need a separate syntax (eg. parens or braces) to delimit any operations that occur within an argument list. I guess you could always have a more standard syntax for general use and offer that syntax as an alternative for non-nested expressions.


Return to “Coding”

Who is online

Users browsing this forum: Yahoo [Bot] and 9 guests