Skip to content

Refinements

Linda_pp edited this page Mar 9, 2017 · 7 revisions

Comment

Starts with #.

Fundamental Type

  • Integer
    • int: 64bit signed
    • uint: 64bit unsigned
    • i32: 32bit signed
    • u32: 32bit unsigned
    • i8: 8bit signed
    • u8: 8bit unsigned
  • Float
    • float: 64bit floating point
    • f32: 32bit floating point
  • Boolean
    • bool
    • true/false
  • String
    • utf8
    • immutable
  • Function
  • Type (Left-hand side of type)
  • Module (Imported package with import)

Complex Types

Tuple

For literal, 1 elem tuple is not allowed.

({elem0}, {elem1}, {elems}...)
  • Named Elements
# Type Declaration
(
    name0: int,
    name1: bool,
)

# Literal
(name0: 42, name1: false)
  • Unnamed Elements
# Type Declaration
(int, bool)

# Literal
(42, false)
  • Mixed

named fields must follow unnamed field

# Type Declaration
(int, bool, name0: float)

# Literal
(42, false, name0: 3.14)

Array

[elem]

Example:

# Type Declaration
[int]

# Literal
[1, 2, 3]
[] : [int]

# Access
[1, 2, 3][1] == 2

Dict

[key => val]

Example:

# Type Declaration
[bool => int]

# Literal
[true => 42, false => 21]

# Access
[true => 42, false => 21][true] == 42

Tagged Union

enum (
    Foo(int),
    Bar(name0: bool),
    Piyo,
)

Type Alias

type {name} = {type}

It defines a new type which is equivalent to rhs.

type Point = (
    x: float,
    y: float,
)

Point(x: 3.14, y: 2.7)

type Maybe = enum (
    Just(?),
    Nothing,
)

var m := Maybe::Just(42)
m = Maybe::Nothing # Inferred as Maybe(int)

Function

func identifier(params) ret
    body
end

It defines a constant variable identifier whose type is function.

# Type of n and return type must be inferred
func product(n)
    ret if n == 0 then 1 else n * product(n-1) end
end

# Type can be explicitly specified
func product(n: uint): uint
    ...
end

# 'var' keyword will pass parameter as mutable value with value semantics
func product(var n)
    n = 10 # OK
end

Calling with positional or named arguments. If mixed, unnamed params must be followed by named ones.

func foo(a, b, c)
end

# Positional
foo(1, 2, 3)

# Named
foo(a: 1, b: 2, c: 3)

# Mixed
foo(1, 2, c: 3)

Variables

Initialize with :=, modify with =. They are immutable by default. They also can be mutable with var (variable) keyword.

# Define immutable variable
n := 42

# Define mutable variable.
var m := 42

# Modify
m = -10

Semantics

Structs, arrays and dicts are allocated to heap and managed by GC. They are used by reference. When complex type values are passed with value semantics, they will be deeply copied. They may be allocated to stack by escape analysis in the future.

Control Expressions

  • Sequence
{
    statements...
}

Last expression is a value of sequence expression. If it's statement, the sequence can't be expression and can only be used as statement.

  • Branch
if cond
 val1
end

if cond
  val1
else
  val2
end

if cond then val1 else val2 end

if cond
  val1
if cond2
  val2
else
  val3
end

If value of if expression is omit, each if clause can have different value type. In other words, if can be statement. When if is used as expression, each clause must have the same type and the type value will be the value of the if expression.

some_func(
    if n > 0
        42
    else
        21
    end
)
  • Pattern Match
case expr
of patt1
of patt2
else
end
  • Loop
# 'while' loop
for cond
  if some_cond
    break
  if other_cond
    continue
  end
end

# Infinite loop
for true
  ...
end

# Range 'for'
for elem in range
    ...
end

Loops must be a statement, not an expression.

Packaging

Export

In foo.dcs

+ func double(n)
    ret n * 2
end

Import

import foo as F
F.double(42)

Types

Apply Operator

Problem

Instead of UFCS, I'm considering apply operator because UFCS does not work well with modules because

import std

# How to use `std.to_float` or `std.to_int`in below UFCS?
42.to_float()
3.14.to_int()

# same as
std.to_float(42)
std.to_int(3.14)
# One idea: partial import
import std.{to_int, to_float}

# Well defined
42.to_float()
3.14.to_int()

With above partial import, we need to specify imported names twice.

Apply Operator $

Try to consider apply operator $. $ is a binary operator where left hand side takes one expression or one tuple and right hand side takes a funciton.

import std

42 $ std.to_int
# Same as
std.to_int(42)

# Use with tuple
(3.0, 2.0) $ std.power # => 9.0
# Same as
std.power(3.0, 2.0)

# Chain

(3.0, 2.0) $ std.power $ std.sqrt $ std.to_int $ std.print_int

# Initialization?
42 $ std.to_int =: i

We can avoid () on single argument. (f(a) v.s. a.f() v.s. a $ f)

Considering Points

  • How this works with do ... end block?