Simplistic, interpreted, dynamic, object-oriented language inspired by Ruby and Coffeescript.
While trying to be expressive, the main focus on alpha_sapphire's syntax is to keep it simple. There is always a trade-off between expresiveness and complexity. For instance:
foo() unless bar == 2
Is more expressive than:
if bar != 2
foo()
end
The downside is that it adds a bit of complexity. Because alpha_sapphire tries to be as small as possible, the first priority is simplicity.
Functions -- or code blocks, are first-class citizens, so you can use them when needed as you wish, a-la Ruby. A simple function looks like this
foo = fn ->
return "Hello, World!"
end
If the function contains only one expression, it can be expressed as such:
foo = fn -> return "Hello, World!"
Also, the return
is optional, the last evaluated expression is always returned.
foo = fn -> "Hello, World!"
You can receive functions as arguments and return them just like any other value.
addOne = fn x -> x + 1
result = [1, 2, 3].map(addOne) # result is now [2, 3, 4]
result = [1, 2, 3].map(fn x -> x + 1) # this also works
# this function returns another function which adds `n`
# to it's argument
addN = fn n ->
fn x -> x + n
end
# addOne could be implemented as such
addOne = addN(1)
You can call functions just like any other language, by using ()
.
foo()
If the function requires no arguments, the parenthesis are not needed.
foo # call function foo
Arguments can be comma separated if you prefer, but only spaces are required
foo(1, 2, 3)
foo(1 2 3) # same
Object oriented programming in alpha_sapphire aims to be as simple as possible. Thus, there is no inheritance or private methods, only mixins.
class Foo
greet ->
"Hello, World!"
end
end
class Bar
include [:Foo]
end
Bar.new.greet
As with functions, if the method is a single expression, it does not need an end
.
class Foo
greet -> "Hello, World!"
end
Attributes are defined using @
. In order to use them outside the class you need to implement getters and setters.
class Person
init name, age ->
@name = name
@age = 22
end
name -> @name
name= name -> @name = name
age -> @age
end
p = Person.new("Mike", 28)
p.name # Mike
p.name = "John"
p.name # John
You are encouraged to use the helper methods get
, set
and get_set
class Person
get_set [:name] # generate `name` and `name=` methods
get [:age] # only generate `age` method
init name ->
@name = name
end
end
Static methods and attributes are shared across all instances
class Foo
shared do
get_set [:age]
init ->
$age = 22
end
some_method ->
"I am #{$bar} years old"
end
end
end
Foo.bar # 22
Foo.some_method
You can access shared stuff in instances too
class Foo
some_instance_method ->
$bar # 2
end
shared
$bar = 2
end
end
a = 2
name = "mike"
let pi = 3.14 # cannot be changed
some_fun = fn ->
2 # implicit return
end
some_fun # call, parens when no args are optional
class MyClass
person = Person.new
greet = fn name ->
"Hi #{name}!"
end
# method call
delegate :name, :person
end
# "static" methods and attributes
class Foo
shared # this is not a method definition, it's a keyword/special construct
name = "Mike"
hi = fn -> "HI #{mike}" # if the function it's a single expression, no need for an end
end
end
Foo.hi
# Mixins, object composition, method delegation, etc
# If it can't find a method on Foo, it will search in Bar, then in Baz.
# So if there is a repeated method, it uses the one which was included first
class Foo
include [Bar, Baz]
end
# Namespaces, just split them
class Foo::Bar
end
Foo::Bar.new
# Arrays
arr = [1, 2, 3]
arr.each(fn -> print it) # when no parameter is specified, default to "it" to first
# parameter received, also, because it's a single statement
# function, no need for an end.
# Hashes
h = { 'key': 1, foo: 'test str', 22: 'example' }
# Conditionals
if true # or unless false
something
else
something_else
end
# Or maybe
true.ifTrue(->
something
end).ifFalse(->
something_else
end)
# Iteration, should be avoided, use each instead
# or 10.times(fn -> print (it + 1))
while true
break
end