# Defnx = 5# Can redefine constants which is not currently in usepi = 5# Operators in brackets(+) = f # Reassigns the operator_ = a # Discards aa = _ # Error# Assignment chaining# Assignment is basically binding value to variablea = (b = 5) + 3 # a=8, b=5a = []b = a # Binds b to the same array (no copy made)c = nothing # Equivalent to None in python
BigInt, BigFloat available but type promotion has to be explicitly stated
Math Operations & Functions
x / y # dividex (division symbol) y # int dividex \ y # inverse dividex ^ y # powerNaN * false # 0.0false * Inf # 0.0x (|| or &&) y # Short circuitedx += 1# Every binary op has a corresponding vectorized dot op# Unary like ! and (root) too[1, 2, 3] ^ 3 # Error[1, 2, 3] .^ 3 # Elementwise mult# Nested dot calls are fused & adjacent are converted to nested formsx .+ 3 .* x.^2 = (+).(x, (*).(3, (^).(x, 2)))# @. macro applies dot op to all elements inside2 .* A.^2 .+ sin.(A) = @. 2A^2 + sin(A)# Updating op too support dota .+= b or @. a += b = a .= a .+ b1 .+ x # Error# NaN is not equal to anythingNaN == NaN # False[1, NaN] == [1, NaN] # Falseisequal([1, NaN], [1, NaN]) # True & should be usedisinf(), isfinite(), isnan() # Other useful funcs# Comparisons can be chained like python & dotted# Order of comparison is undefined0 .< A .< 11 < 2 <= 3 # true1 > 2 <= 3 # false# Op precedence (:x are symbols)Base.operator_precedence(:+) # 11Base.operator_associativity(:-) # :left# Numeric conversionsT(x) or convert(T, x) # Int8(323) - Errorx % T # Converts int types, overflows if too big w/o errorround(Int, x) = Int(round(x))# sinpi, cospi provide better repr of sin(pi*x)a = sinpi(x)a = sind(x) # In degrees (default is radian)
# Chara = 'x'b = Int(a) # 120, Int64c = '\u0ff' or '\U10ffff'd = '\n' or '\t'# Stringsa = "hi" or """hello"""b = "hellllooooo \ hi"c = """jefej ejofefj"""c = a[1] or a[begin] or a[end]d = b[1:2] # Range indexing & makes a copye = SubString(b, 1, 2) or @views b[1:2] # Creates a view, not a copyfor c in s println(c)endnextind(), prevind(), eachindex() # To find valid indicescodeunit(s, i) # Access raw codeunit# Concatenationc = string(a, ", ", b)c = a * ", " * b # * used rather than + to show noncommutativity# Interpolationa = 234b = "$(a + 1) $a"# Functionsfindfirst(c, s), findlast(c, s), findnext(c, s, i)occursin(s1, s2)repeat(s, times), replace(s1, func)join(vec, s, last_s)length(s)# Regexreg = r"^\s" or Regex(str)occursin(reg, s), match(reg, s)# Byte array literalsbal = b"hello" # UInt8 array# Version literalsver = v"0.2"# Raw literalsr = raw"huehfu" # Extra quotation should be escaped
String supports full UTF-8
All string types are subtypes of AbstractString & all char types of AbstractChar (It is better to use these in function definitions)
Strings are immutable
Strings are byte indexed, not char indexed
Characters are 32 bit primitive
Indexing starts from 1, not 0
Not every index into a String is necessarily a valid char
Number of chars in String is not always same as last index (due to variable length encoding)
Functions
# Definitionsfunction f(x, y) x + yendf(x, y) = x + y# Callingf(2, 3)g = fg(2, 3)# Arg behaviourfunction f!(x::Vec, y::Integer) x[1] = 5 # Mutates y = 7 # No mutation of og object# Return types# Return type enforced and return value is convertedfunction g(x) :: Int8 xend# Operator fn# Except for &&, || all other can be used as fn+(1, 2, 3) # 6# Anon fnx -> x^2 + x + 1() -> 3(x, y) -> x + y# Tuplesa = (1, 2, 3) # Immutable, fixed length containersb = (1,)println(a[0])# Named tuplesx = (a=2, b=3)x[0] or x.a# Destructuring(a, b, c) = [1, 2, 3] # RHS has to be an iterable_, _, c = [1, 2, 3]arr[0], b = [1, 2] # arr[0] is mutated# Slurping# Assignment of a collection of remaining elementsa, b..., c = "hello" # a=h, b=ell, c=o# Argument destructuringf((x, y)) = x + yf((2, 3))# Property destructuringfoo((; x, y)) = x + yfoo((x=2, y=2)) # Advantage is that both structs and namedtuples can be passed# Varargs# In definitionfoo(a, b, x...) = x # x is a tuple# In callerx = (2, 3, 4)foo(1, x...) # a=1, b=2, x=(3, 4), the callee need not be a varargs function# Optional argsfunction x(a, b=1, c=1) aendx(1)x(1, 2)x(1, 2, 3)methods(x) # Shows all possible methods of fn x# Keyword argsfunction x(a, b; c=1, d=1, kwargs...) # kwargs collects extra keyword args (a immutable key value iterator) aendx(1, 2, c=1, d=2)x(1, 2, c=1, :d=>2)# In case kwargs has same name as other keyword arg, that arg takes pref# Concise do blocks# do creates an anon fn# do a, b = 2 args & do (a, b) = tuple arg# do can capture vars from outer scope and automatically close streams, etcmap(x->x, [])map([]) do x xend# Composition & piping(f \circ g)(x) = f(g(x))1:10 |> sum |> sqrt["a", "b"] .|> [uppercase, lowercase]
Functions are first class objects that map tuple of arguments to a return value
Not pure
No explicit return required
Values are not copied to arguments, rather are shared. Arguments act like new bindings to the values (like Python)
Mutating function identifiers end in !
Reasons to have arg types
No perf impact
Correctness & Clarity
Dispatch - can have different versions of a fn for diff arg types
Do not overly restrict types & omit when not sure
Return types are not usually used and instead automatically inferred
Operators with special names
Control Flow
# Compound exprsz = begin x = 1 y = 2 x + yendz = (x=1; y=2; x+y)# Conditional evalif x < y ...elseif x > y ...else ...enda ? b : c# Short circuitsn >= 0 || error("n must be positive")# Loopsi = 0while i <= 3 global i += 1endfor i = 1:3 # 1:3 is a range object ...endfor i in [] ...endfor i = 1:2, j = 3:i # Nested loop, break exits both loops ...endfor (i, j) in zip([], []) ...# Exceptionsstruct Custom <: Exception var::Symbolend # Custom exceptionthrow(DomainError(1, ""))error("") # Stops executiontry ...catch e if isa(e, DomainError) ...else # Used instead of extending try so as to not catch more errors ...finally ...end
if blocks are leaky, i.e they do not introduce a local scope. New variables defined can be used later
if blocks return values
1/0 do not convey boolean meanings
&&, || are short circuited, second exprs are not evaluated if first satisfies the operator
&, | are not short circuited
Scope
# Local scope# Constants (best used with globals, local consts are not supported)const pi = 3.14const a, b = 1, 2x::Float64 = 2.7 # Automatically a constant if globally declared
Two types - global and local
if does not introduce a new scope
Inner scopes can see variables in outer scopes
Modules
Introduce new global scopes
Variables present can't be mutated from other modules directly
Constants
Not supported by local vars
Structs are const by default
Types
# Types(1+2)::Int # Asserts typea::Int = 1 + 2 # Specifies/Declares type# Abstract typesabstract type Number endabstract type Signed <: Integer end # <: specifies supertype (defaults to Any)Integer <: Number # true# Primitivesprimitive type Float16 <: AbstractFloat 16 end # Specify bits# Composite typesstruct Foo bar baz::Intendmutable struct A a const b::Intendfoo = Foo("", 1) # Constructorfoo.bar # ""fieldnames(Foo) # (:bar, :baz)# Type unionsIntOrStr = Union{Int, AbstractString}Option = Union{Nothing, Int} # Like Option<T># Parametric types (types with parameters)# Compositestruct Point{T} x::T y::TendPoint{AbstractString} <: Point # truePoint{<:AbstractString} # Subtypes also accepted nowPoint{Float64}(1.0, 2.0)# Abstractabstract type Pointy{T, X} endabstract type Pointy{T<:Real} end # Guard# Tuple typesTuple{Int, String}Tuple{Int, Vararg{String}} # Variable arg type# Named tuple types@NamedTuple{a::Int, b::String}@NamedTuple begin a::Int b::Stringend# UnionAll types
Julia is dynamically typed, is nominative and parametric
Has method dispatch with on types of args
All concrete types are final and can only have abstract types are supertypes
All values in Julia are true objects
No compile time type exists
Variables don't have types, only values do
Abstract types
Can't be instantiated
Any is at the top of the type graph and Union{} at the bottom
Gives default impls for concrete types (make it generic)
Primitive types
Do not make your own
struct is immutable but contained objects that are mutable remain mutable. Mutables are alloc on heap
All 3 types are repr internally as DataType
Type params are invariant
Float64 <: Real but P{Float64} not subtype of P{Real}
Definition of one possible behaviour of a function is called a method
When a function is applied to a particular tuple of arguments, the most specific method applicable to those arguments is applied.
The choice of which method to execute when a function is applied is called dispatch.
Multiple dispatch used where all args are considered (unlike other langs where only first considered)
The first method definition for a function creates the function object, and subsequent method definitions add new methods to the existing function object. Creating multiple methods is called specialization
Other than explicit method decl, Julia implicitly creates methods at compile time based on arg types
Modules
module FastThingsend# QualificationBase.sin()Base.:+ # Needs a colon for operators# Exportsmodule Aexport b, Cconst C = 5b(x) = xend# Using modulesusing A # Adds elements of export list to global namespaceusing .A # Load module from locally defined moduleimport A # Allows only qualified access (export list does not matter)import A as Bimport A:a as busing .A:a, b, A # Specific name imports (specify modulename itself to import it too)# Adding methodsusing .A or import .A:a # using .A:a won't workA.a() = ...# Baremodules (w/o containing Base, eval and include)baremodule A# Submodulesmodule Amodule Bimport ..C:a # Each . = parentendend
They are separate namespaces and introduce a global scope
Can export and import names
Can be precompiled
Can have multiple modules per file and vice versa
PascalCase and plural
Standard modules
Core - builtins
Base - base functionality
Main - top level and current module
Documentation
"Info" # Interpreted as markdownfoo(x) = x# Enables to use raw, and other types of string including interpolation@doc raw""""""f(x) = x@doc(@doc foo!)foo # Use foo!'s documentation
Metaprogramming
Lisp style macros operating at the level of AST
Arrays
zeroes(Int8, (2, 3))ones(...), trues(), falses()reshape(), copy(), deepcopy()range(), fill!()Matrix{T}()# Array literals[1, 2, 3] # Tries to promote if diff types, else heterogeneousInt8[1, 2, 3]# Concatenation[1:2, 4:5] # 2D array[1:2; 4:5] # Vertically concats (1, 2, 4, 5)'[1:2;; 4:5] # Horizontally concat (1, 2, 4, 5)# Comprehensions[x + y for x=1:2, y=2:3 if x + y == 2][(i,j) for i=1:3 for j=1:i] # Nested loop# Generators(i*2 for i=1:10)# Indexinga[1, 2]a[(1, 2)]x[2:3, 2:end-1] # Indexed assignmentx[2, 3] = 1x[CartesianIndex(1, 2, 3)] # Groups indices into one objx[[false, true], :] # Boolean indexingx[map(ispow2, x)]x[5] # If x is multidim, a single element is returned in column major form