Skip to content

Instantly share code, notes, and snippets.

@mythmon
Last active December 16, 2015 22:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mythmon/5507348 to your computer and use it in GitHub Desktop.
Save mythmon/5507348 to your computer and use it in GitHub Desktop.
So I have this idea. I want to make a programming language. I have some goals. Lets see how this works out.

Blocks

Blocks are kind of like functions. The store a sequence of expressions to be evaluated later. They are a value, so like any value they need to be bound to a name to be stored.

let first = {
    print 'Hello, world!'
}

Blocks can be called

first()
# Hello, World!

Blocks can receive values from their environment

let greet = {
    in name
    print "Hello, ${name}!"
}

print greet('Mike')
# 'Hello, Mike!'

Blocks can give output to the environment

let add = {
    in a, b
    out a + b
}
add(1, 2)
# 3

Blocks can give have multiple outputs

let parseColor = {
    in hexCode # String like '#RRGGBB'
    let r, g, b
    r = int(hexCode[1:2], 16)
    g = int(hexCode[3:4], 16)
    b = int(hexCode[5:6], 16)
    out r, g, b
}

parseColor('#ff8800')
# [255, 136, 0]

Blocks can have structured outputs

let getNames = {
    in id
    let user = getUser(id)
    out first => user.first_name, last => user.last_name
}

first_name from getNames(42)
# 'Douglas'

Blocks can be bound by name

let say {
    in to, msg
    out "${to}: ${msg}"
}

say('msg' => 'Hello', 'to' => 'Mike')
# Mike: Hello
say('to' => 'Sarah', 'msg' => 'Hello')
# Sarah: Hello

Blocks are values

let do = {
    in func, value
    out func(value)
}

let isEven = {
    in value
    out value % 2 == 0
}

do(isEven, 10)
# true

Data structures

Lists

let data = [
    {'year': 2010, 'ants squashed': 200, 'ant bites': 400},
    {'year': 2011, 'ants squashed': 300, 'ant bites': 450},
    {'year': 2012, 'ants squashed': 600, 'ant bites': 500},
    {'year': 2013, 'ants squashed': 1000, 'ant bites': 550},
]

data.where({ in row; out row['ants squashed'] > row['ant bites'] })
    .map({ out (in row)['year'] })
# [2012, 2013]

Maps

let colors = {
    'apple' => 'red'
    'orange' => 'orange'
    'banana' => 'banana'
    'cherry' => 'red'
}

colors['apple']
# 'red'

colors.where({ out (in _, v).color == 'red' })
# ['apple', 'cherry']

colors.where({ out (in _, v) == 'orange' }).map({ out (in s).uppercase() })
# ['ORANGE']

Ranges

(1:10).reduce(0, { out (in memo) += (in n) })
# 55

Notes

  • Blocks being passed as anonymous functions.
  • Getting data in and out is awkward.
  • Most data structures provide methods like where, map, and reduce.
  • In expressions with one name evaluate to the value of that name.
  • underscores in in expressions are throwaways.

Here I will write some solutions to Euler problems using this language. If something is inconsistent, then I messed up. Also, maybe these algorithms aren't perfect, but it provides a feel for the language.

Problem 1: Multiples of 3 and 5

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.

let li = []
for i from 1:1000 {
    if i % 3 == 0 or i % 5 == 0 {
        li.push(i)
    }
}
print(li)

Problem 2: Even Fibonacci numbers

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.

let fib = {
    in upperLimit
    let a, b = 0, 1
    while a < upperLimit {
        yield a
        a, b = b, a + b
    }
}
a, b = 0, 1
sum = 0

for f from fib(4000001) {
    if f % 2 == 0 {
        sum += f
    }
}

print(sum)

Problem 3: Largest prime factor

The prime factors of 13195 are 5, 7, 13 and 29.

What is the largest prime factor of the number 600851475143 ?

let isPrime = {
    in n
    if n % 2 == 0 {
        out false
    }
    stop = sqrt(n)
    for i from 3:stop {
        if n % i == 0 {
            out false
        }
    }
    out true
}

let num = 600851475143
let test = iter(2:num)
let current = test.next()
let largestFactor = null

while num > 1 {
    if num % current == 0 {
        current /= num
        largestFactor = current
    } else {
        current = test.next()
    }
}

print(current)

Problem 4: Largest palindrome product

A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.

Find the largest palindrome made from the product of two 3-digit numbers.

let isPalindrome = {
    in num
    let asStr = str(num)
    mid = math.ceil(str.length / 2)
    for i from 0:mid {
        if asStr[i] != asStr[-i] {
            out false
        }
    }
    out true
}

# Pretty ineffecient to go through all the values of x and y, when but this
# is simple.
threeDigit = 100:999
max = 0
for x from threeDigit, y from threeDigit {
    if isPalindrome(x * y) {
        max = math.max(x * y, max)
    }
}

print(max)

Recursion?

Probably. Recursion is cool business, but the whole "all blocks are anonymous" makes it kind of tough. Ideas:

Magically bound named recursion

Just make the name of the function available to itself.

let factorial = {
    get n
    if n <= 1 {
        out 1
    } else {
        out factorial@(n - 1)
    }
}

Good

  • Familiar

Bad

  • What if the name factorial is rebound?

Recursion keyword

Give an explicit keyword that makes a recursive call to the current function, whatever that may be.

Ideas for keyword: recurse, recur, ???

let factorial = {
    get n
    if n <= 1 {
        out 1
    } else {
        out recurse@(n - 1)
    }
}

Good

  • No binding problems

Bad

  • Weird
@relud
Copy link

relud commented May 3, 2013

Recursion thought:

every name has a path. use . as your path separator. leading . refers to global.

examples:

print self.path
# .
print parent.path
# .

let factorial = {
  in n
  if n <= 1 {
    out 1
  } else {
    out n * self@(n - 1)
  }
}

let location = {
  print path
}

let work = {
  print self.path
  # .work
  print parent.path
  # .
  n = 12
  factorial = .factorial
  factorialn = parent.factorial@n
  print n.path
  # .work.n
  location = .location
}

print work.factorial.path
# .work.factorial
print work.factorialn.n
#12
print work.n.path
# .work.n
print work.n
#12
location
# .location
work.location
# .work.location

print work.factorial.n
# undefined

and for objects

o = { }
n.n = 3
let o.factorial = {
  if .factorial@parent.n
}

o.factorial
#6

also valid equivalent

o = {}
o['n'] = 3
o['factorial'] = .factorial@parent.n

o = {
  'n': 3,
  let 'factorial': {
    out .factorial@parent.n
  },
}

@relud
Copy link

relud commented May 3, 2013

alternately you could have:

path(self)
path(parent)
path(work.factorial)
path(work.factorial.n)
path(.work.factorial.n)

etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment