Skip to content

Tip

This section is a bird view of U syntax. Please read the overview and check Syntax for more details.

U properties are:

  • U syntax is designed to be concise and expressive,
  • Source code is UTF-8 encoded,
  • Comments, characters and string literals can contain Unicode characters,
  • Default character value is as wide as a Unicode character: up to 4 bytes,
  • No direct keyword: anything that starts with colon : or \ is a special parser hint called aperture,
  • U keywords are guarded with ::, like ::if,
  • U is whitespace sensitive for good reasons. See Syntax,
  • U uses curly braces {} to denote functions. U does not use indentation for good reasons. See Braces vs Indentation,
  • Everything is either a value or an expression,
  • Values are immutable by default, but you can make them mutable,
  • U has OOP with a more flexible syntax that others languages,
  • Most behaviors can be customized by build system ugo.

Comments

# This is a single line comment
#(
  This is a comment
  written over 
  multiple lines
#)

Execute Without Main

\< "Welcome" 
# Print "Welcome"

Execute With Main

# Main function
\/ : {
    \< "Welcome" 
} 

Statements

U is line-based, with optional semicolons because it's visually pleasing.

Semicolons are only necessary if you want to write multiple statements on a single line.

:a : 1
:b : 2
# In one line
:a : 1; :b : 2

Symbols

Symbols start with colon : followed by a name.

:var : 2   # ':var' is a Symbol named 'var', compiler will convert it to a number
f var - 3  # 'f', 'var' are Identifiers

:"a -> b" # With spaces
:1234     # From a number
:'*'      # From operator

:name : :foo
:(name)   # From an expression
:\'A'     # From a Char

:timon : "Timon, U's logo"

# Unicode
:丁满 : "丁满 in chinese"
:Тимон : "Тимон in russian"
:🐨 : "koala that looks like Timon"
 : 3.14159

# Operator in names
:is_enabled?
:shift->> : # function 
:'+++' ...

Identifier

Identifiers are reference names.

shift->> "hello"
# Call "shift->>" with string "hello"

Delimiters

In U, delimiters are Values:

  • (...): lexical level grouping
  • {...}: define a function, closure, or any construct that need to execute statements
  • [...]: define a data model that needs storage at compile time or at run time like sets, array, hash map

See below for examples.

Variables

In U, variables are defined with colon : and a name.

:a : 88  # Immutable
:b :! 42 # Mutable
::mut :c :! 10 # Mutable

Types

U infers value types. You don't need to specify them unless the compiler needs your hints.

Types starts with @ for user defined type, @ for built-in types:

:x : @int64 # Built-in 'int'
:model : @Model3D # user defined model

# Custom type - Imperative style
@Array-of-string-int-pair @[@string, @int]

No Null Value

U has no concept of null only optional types.

For example:

:result : @? fun-call ...
#same as  
:result : ::some fun-call ...
# 'fun-call' might not return a value

Booleans

:t : @true
:f : @false

Numbers

# Integer
1234    # Integer
1_234   # Integer

# Floating point: FP
12.34   # Decimal FP
1234e-5 # Decimal FP

# Hexadecimal FP
0x1.FFp4

Characters

Characters start with escape \ followed by quotes:

:a : \'A'
:b : \"'"
:c : \'❤️'
:d : \'\x81'
:e : \'u{41}'

Strings

In U, three quote types denote values:

  • '...': single quote,
  • "..." : double quote,
  • `...` : backtick or left quote.
:name : 'Alice' # Immutable
:name :! 'Alice' # Mutable

\< "Welcome " + name
# Prints "Welcome Alice" 

:%-"
  this is a 
  multline string
%"

'a'.%ascii   # Convert to ascii
'a'.%ascii:code # Get ascii code 
'a'.%U:scalars # Get utf8 scalars

String Interpolation

Without spaces, use \:

:last_name : 'Simpson'

:user_name : 'Lisa'
\< 'Welcome \:name' 
# Prints 'Welcome Lisa'
With spaces, use \( and \) or any delimiter:

:mother : 'Marge'
\< 'Welcome \(' mother '\) \:last_name!'  

String Constructors

String Constructors let you define string behaviors before creating it. It allows powerful optimizations. String Constructors start with :% for immutable strings, and :%! for immutable strings.

:str : :%(max: 5)'peace'
# Immutable string with a maximum capacity of 5 characters

:str : :%{: lhs; ... }'peace'
# Immutable string with in line constructor. 'lhs' (left hand side) is 'peace'

Ranges

0:.:2   # 0,1,2
0:.<:2  # 0,1
0:<.:2  # 1,2
0:<.<:2 # 1

0:+2:6 #[0,2,4,6]
6:-2:0 #[6,4,2,0]

# Iterator
'a':.:'z' {: v
    \< v
}

Regexp

:line : :/[^\n]+/
# Escaping '/'
:path : :/\/?(\w+\/)+/

# Without escaping '/', use '{}'; useful when parsing URLs
:line : ::R{/?(\w+/)+}

Variants or Enums

Variants starts with keywords ::Variant, ::V, ::Enum, or ::E. Variants can have methods.

::V :Day, [
        :Monday: 1,
        :Tuesday, :Wednesday, :Thursday, :Friday, :Saturday,
        :Sunday
    ], {
        # Define methods
        :to_str :  {?
            :- Monday: "Monday"
            ...
        }
    }

Classes

Classes are defined keyword ::A or ::An. Inheritance, Composition, Aggregation have special meaning in U, and are not the same as C++/Java. Please refer to syntax.

# class 'Position' with attribute 'x' as int
::A :Position, :x: @int, ...

::A :Position, {
    # Constructors
    (:x, :y) : (... @Int)
}, {
    # Body if needed
    :move : ...
}, ... # Options

Objects

Objects are defined with keywords ::a, ::an, or Object::new.

# class 'Position' with attribute 'x' as int
::A :Position, :x: @int, ...

:pos1 : ::a @Position, ...
:pos2 : @Position::new ...

pos2.x = 1

Collections

Collections are structures that group values. Collections are 0-based indexed.

Sequences

(a, b)   # Sequence
(:a, :b) :.. (1, 2) # Sequence. Same as :a : 1; :b : 2

Arrays

:a : [1, 2] 
:a : [1, "hello"] 
#Fixed size: size <= 10
:b : [<= 10; ...]`

Lists

:l : [- a, b -]

Sets

[$  'a', 'a', 'b' $] # Set accepting duplicates
[$! .... $] # Set without duplicates

Hashes

:h :! [< :name: 'Megan', :role >]  # Shorthand for ':role: :role'
\< h[:name]
# Prints 'Megan' 

h[:name] := 'Francis'
\< h[:name]
# Prints 'Francis' 

Matrices

 : 3

:mat_4_4 : :%|
  1 2   3
  4 \#φ 5 # Use variable <φ>
  5 6   7
%|

#Same as
:mat_4_4 : [|
  1,2 , 3;
  4, φ, 5; 
  5, 6, 7
|]

Tables

Tables are collections of columns. Columns can have different sizes.

:var : 3
:t : :%+
    :a  4
    :b  var
    :a  2
    :d  1
%+
Which is the same as:
:var : 3
:t : [+
  [:a, :b, :a, :d],
  [4, var, 2, 1]
+]

Then:

\< t.cols-names= :c1, :c2

#(
Prints
C1 | C2
---|---
a    4 
b    3
a    2
d    1
#)

#In-place modifications
t.first.sort-asc!
t.last.sort-asc!

\< t
#(
Prints
C1 | C2
---|---
a    1 
a    2
b    3
d    4
#)

\< t@json
#(
Prints
{"a": 2, "b": 3, "d": 4}
# "a" with value 1 overwritten
#)

Collections Iterators

[0,1,3].{: element;
    ...
}

Collections Access

:a :! [0, 'blue' , 2] 
a[1] # 'green'
a[0] := 'red'

a <<= 'green' # ['red', 'blue' , 'green'] 

#With default value
# Return element at 5 or a default string
\< a[5, 'no value']
# Prints 'no value'

Collections Specifiers

:a :! [:( # Specifiers start
   @int, # All elements must be ints
   :capacity: 2, # Only 2 elements
   :before_create: :> ...., # Before creating the collection
   :'before_<<': :> e :: !? e.odd # Ensure that all items are even; 'before-<<' means 'before-pushing'
  ): # Specifiers end

  # ... elements
]

a <<= 0 # OK
a <<= 1 # OK
a <<= "hello" # Error: only ints
a <<= 2 # Error: max 2

Collection Comprehension

Collection Comprehensions are special kind of specifiers.

\< [% x ; 1:.:10; x % 2 == 0 %]
# Prints [2, 4, 6, 8]

Collection Slices

:a : [0,1,2, 3, 4, 5, 6]

:b : a[1:.:4]  # Slice from 1 to 4
:c : a[0:+2:4] # From 0 to 4 by +2; 'c' <=> [0,2,4]

Function and Closures Definitions

Signature

Signatures consist of the function’s parameters, return type, and optional specifiers.

:add : {: ...parameters;
  ...
}

add arguments
#'arguments' must match 'parameters'

Optional Return

By default, the return value is the last statement one. But you can use ::return keyword, or \^.

:add : {:
  #implicit return
  3 + 2
}
# Explicit 
:add : {: 
   :result : ...
   \^ result
   # Or
   ::return result
}

All return statements start with \^. The arrow visually depicts a return to the origin of the function ^.

In-Line Function: no signature

Use :> or ::>.

:print_app_name : :> \< 'Game One'

In-Line Function: with signature

Use :>: or ::>: to start definition, and :: to separate parameters and body.

# Signature: parameters 'speed', 'goal'; infered return type as 'string'
:walk ::>: speed, goal :: "Robot walking at \:speed miles/h, to \:goal..."

# Same as Above with parentheses
:walk ::>( speed, goal ) "..."

# Signature: return type 'string'; parameters 'speed' as int, 'goal' as string;
:walk ::>: @string <- @int speed, @string goal :: "..."

Block Functions: no signature

:f : { 
    \< "hello"
}

Block Functions: with signature

# Multiple lines
:add : {: x, y; # Parameters
    x += 1
    x + y
}

# Same as this single line
:remove : {: @int <- @int x , @int y ; # Full Signature
}

Anonymous Functions

\< {: a, b; a + b }. 1, 2
# Prints '3'

# Same as
\< {: a, b; a + b }(1, 2)

# Same as
:fn : {: a, b; a + b }
\< fn(1, 2)

Closures

:add : {: a, b
    # Returning a closure that capture 'a', 'b'
    :>: c :: a + b + c
}

:fc : add 1, 2 # return :>: c :: 3 + c
\< fc 3
# Prints '6' <=> 3 + (c == 3)

Function Calls

# 'f', 'a', 'b', 'c' are functions
f a b c 
# Same as
f( a( b( c() )))
# Same as
f. a, b, c

Pipeline

\< square inc 5 # Same as square( inc( 5))
# Prints 36

# With pipeline
\< 5 |> inc |> square
# Prints 36

# With method chaining
\< 5.inc.square
# Prints 36

Pipeline operator is very useful for array processing where the passed value is always an array.

Function Calls with variable function names

:run ::>: speed :: ...
:stop ::> ...

:a : :run
. a, 1 # Call run
:a : :stop
.(a) # Call stop

Positional, Labeled, Optional Arguments

Positional and optional parameters separated by a slash /.

:move ::>: speed, direction : Forward / light-on, sound-on :: ...

# 'light-on', 'sound-on' are optionals parameters

move 1
move 2, Upward
move 2, Upward, :sound_on: true
move 2, Upward, :light_on: true, :sound_on: true

Variable Length Arguments

:add ::>: ..args  :: ...

add 1    # 'args' <=> [1]
add 1,2  # 'args' <=> [1,2]

Recusive function

::rec :factorial : {:
    :? n {
        :- 0, 1
        :-* n * factorial n-1
    }
}

Mutually recusive function

::rec {
    :even :>: n == 0 ? true, odd n - 1
    :odd  :>: n == 0 ? false, even n - 1
}

Defer

:openFile : {: 
    :f : @File.open 'log.txt', ::defer f.close
    # Same as 
    :f : @File.open 'log.txt', :close: :defer
}

Concurrency

# Start 10 workers
:w : :|.starts 10, :workers ...
# Or
:w : :|.workpool 10


\< w.:|idle-workers
# Prints a list of idle-workers

Sequents (Results)

U allows you to return an execution value, and a status value about the function execution called a sequent.

# Define an sequent called <login-status>
@!login-status :Ok, :Error

:login : {: name
   ...
   # Return 
   \^! Ok, db.user name
}

login.! :Alice, {!?: status, user
    # Deal with the status.
    :- Ok, ...
    :- Error, ...
}

Exceptions

login : {!> ::Exception :MyException # <login> can throws MyException
   ...
   # Throws my exception
   :!> MyException, "Exception should be catched elsewhere"
}

# Call with exception management
login.!> 'Bob', {
    :!- MyException, {
        ...
    }
}

Pointers

Pointer directives starts with :&.

From typed memory locations to values, use :&< (address at):

    # Map Raspberry GPIO led at memory location 0x3e000000
    # ':!' bind mutable variable
    :led :! :&<int 0x3e000000
Same as led = &(0x3e000000) in C.

From values to typed memory locations, use :&> (content at):

    # Switch on LED
    :&> led := 1
    # Or
    led :&= 1
Same as *led = 1 in C.

Documentation

U has documentation in a markdown and custom format:

#[md
    # Function Move
    Function **move** copy object...
#]

Tests

U has builtin test and validation constructs:

#?{
   # Expecting result == 4
   add 3, :>: input, result ; result == 4
#} 
:add :>: v ; v + 1

UC

UC converts C/C++ codebases to U code.