learnxinyminutes-docs/citron.md
Boris Verkhovskiy 434f15cd10 .markdown -> .md
2024-12-08 20:03:01 -07:00

5.9 KiB

language filename contributors lang
citron learncitron.ctr
AnotherTest
en-us
# Comments start with a '#'
# All comments encompass a single line

###########################################
## 1. Primitive Data types and Operators
###########################################

# You have numbers
3. # 3

# Numbers are all doubles in interpreted mode

# Mathematical operator precedence is not respected.
# binary 'operators' are evaluated in ltr order
1 + 1. # 2
8 - 4. # 4
10 + 2 * 3. # 36

# Division is always floating division
35 / 2 # 17.5.

# Integer division is non-trivial, you may use floor
(35 / 2) floor # 17.

# Booleans are primitives
True.
False.

# Boolean messages
True not. # False
False not. # True
1 = 1. # True
1 !=: 1. # False
1 < 10. # True

# Here, `not` is a unary message to the object `Boolean`
# Messages are comparable to instance method calls
# And they have three different forms:
#   1. Unary messages: Length > 1, and they take no arguments:
        False not.
#   2. Binary Messages: Length = 1, and they take a single argument:
        False & True.
#   3. Keyword messages: must have at least one ':', they take as many arguments
#      as they have `:` s
        False either: 1 or: 2. # 2

# Strings
'This is a string'.
'There are no character types exposed to the user'.
# "You cannot use double quotes for strings" <- Error

# Strins can be summed
'Hello, ' + 'World!'. # 'Hello, World!'

# Strings allow access to their characters
'This is a beautiful string' at: 0. # 'T'

###########################################
## intermission: Basic Assignment
###########################################

# You may assign values to the current scope:
var name is value. # assigns `value` into `name`

# You may also assign values into the current object's namespace
my name is value. # assigns `value` into the current object's `name` property

# Please note that these names are checked at compile (read parse if in interpreted mode) time
# but you may treat them as dynamic assignments anyway

###########################################
## 2. Lists(Arrays?) and Tuples
###########################################

# Arrays are allowed to have multiple types
Array new < 1 ; 2 ; 'string' ; Nil. # Array new < 1 ; 2 ; 'string' ; Nil

# Tuples act like arrays, but are immutable.
# Any shenanigans degrade them to arrays, however
[1, 2, 'string']. # [1, 2, 'string']

# They can interoperate with arrays
[1, 'string'] + (Array new < 'wat'). # Array new < 1 ; 'string' ; 'wat'

# Indexing into them
[1, 2, 3] at: 1. # 2

# Some array operations
var arr is Array new < 1 ; 2 ; 3.

arr head. # 1
arr tail. # Array new < 2 ; 3.
arr init. # Array new < 1 ; 2.
arr last. # 3
arr push: 4. # Array new < 1 ; 2 ; 3 ; 4.
arr pop. # 4
arr pop: 1. # 2, `arr` is rebound to Array new < 1 ; 3.

# List comprehensions
[x * 2 + y,, arr, arr + [4, 5],, x > 1]. # Array ← 7 ; 9 ; 10 ; 11
# fresh variable names are bound as they are encountered,
# so `x` is bound to the values in `arr`
# and `y` is bound to the values in `arr + [4, 5]`
#
# The general format is: [expr,, bindings*,, predicates*]


####################################
## 3. Functions
####################################

# A simple function that takes two variables
var add is {:a:b ^a + b.}.

# this function will resolve all its names except the formal arguments
# in the context it is called in.

# Using the function
add applyTo: 3 and: 5. # 8
add applyAll: [3, 5]. # 8

# Also a (customizable -- more on this later) pseudo-operator allows for a shorthand
# of function calls
# By default it is REF[args]

add[3, 5]. # 8

# To customize this behaviour, you may simply use a compiler pragma:
#:callShorthand ()

# And then you may use the specified operator.
# Note that the allowed 'operator' can only be made of any of these: []{}()
# And you may mix-and-match (why would anyone do that?)

add(3, 5). # 8

# You may also use functions as operators in the following way:

3 `add` 5. # 8
# This call binds as such: add[(3), 5]
# because the default fixity is left, and the default precedence is 1

# You may change the precedence/fixity of this operator with a pragma
#:declare infixr 1 add

3 `add` 5. # 8
# now this binds as such: add[3, (5)].

# There is another form of functions too
# So far, the functions were resolved in a dynamic fashion
# But a lexically scoped block is also possible
var sillyAdd is {\:x:y add[x,y].}.

# In these blocks, you are not allowed to declare new variables
# Except with the use of Object::'letEqual:in:`
# And the last expression is implicitly returned.

# You may also use a shorthand for lambda expressions
var mul is \:x:y x * y.

# These capture the named bindings that are not present in their
# formal parameters, and retain them. (by ref)

###########################################
## 5. Control Flow
###########################################

# inline conditional-expressions
var citron is 1 = 1 either: 'awesome' or: 'awful'. # citron is 'awesome'

# multiple lines is fine too
var citron is 1 = 1
    either: 'awesome'
    or:     'awful'.

# looping
10 times: {:x
    Pen writeln: x.
}. # 10. -- side effect: 10 lines in stdout, with numbers 0 through 9 in them

# Citron properly supports tail-call recursion in lexically scoped blocks
# So use those to your heart's desire

# mapping most data structures is as simple as `fmap:`
[1, 2, 3, 4] fmap: \:x x + 1. # [2, 3, 4, 5]

# You can use `foldl:accumulator:` to fold a list/tuple
[1, 2, 3, 4] foldl: (\:acc:x acc * 2 + x) accumulator: 4. # 90

# That expression is the same as
(2 * (2 * (2 * (2 * 4 + 1) + 2) + 3) + 4)

###################################
## 6. IO
###################################

# IO is quite simple
# With `Pen` being used for console output
# and Program::'input' and Program::'waitForInput' being used for console input

Pen writeln: 'Hello, ocean!' # prints 'Hello, ocean!\n' to the terminal

Pen writeln: Program waitForInput. # reads a line and prints it back