Calling C and Fortran

You can call external compiled code directly from Julia using ccall.


In [2]:
# C signature:
# char *getenv(const char *name)
#
# Julia syntax:
#      ccall(function, return_type, (input_type, intput_type, ...), input_value, input_value, ...)   
#
path = ccall(:getenv, Ptr{Uint8}, (Ptr{Uint8},), "SHELL")
bytestring(path)


Out[2]:
"/bin/bash"

In [3]:
# call from any shared library (.so, .dylib, .dll)
mysin(x::Any) = ccall((:sin,"libm"), Cdouble, (Cdouble,), x)


Out[3]:
mysin (generic function with 1 method)

In [4]:
mysin(3.0)


Out[4]:
0.1411200080598672

In [5]:
# ccall automatically converts the types (if possible)
mysin(3)


Out[5]:
0.1411200080598672

In [6]:
code_native(mysin, (Float64,))


	.text
Filename: In[3]
Source line: 2
	push	RBP
	mov	RBP, RSP
	movabs	RAX, 140560087283456
Source line: 2
	call	RAX
	pop	RBP
	ret

In [7]:
# The standard library uses ccall as well
@which sin(3.)


Out[7]:
sin(x::Float64) at math.jl:122

In [8]:
# Code can be "vectorized" on the Julia side.
mysin(x::Array{Float64, 1}) = [mysin(xi) for xi in x]


Out[8]:
mysin (generic function with 2 methods)

In [10]:
mysin("hi")


`convert` has no method matching convert(::Type{Float64}, ::ASCIIString)
while loading In[10], in expression starting on line 1
 in mysin at In[3]:2

In [11]:
# There are macros to reduce boilerplate
@vectorize_1arg Real mysin


Out[11]:
mysin (generic function with 5 methods)

In [12]:
methods(mysin)


Out[12]:
5 methods for generic function mysin:
  • mysin(x::Array{Float64,1}) at In[8]:2
  • mysin{T<:Real}(::AbstractArray{T<:Real,1}) at operators.jl:359
  • mysin{T<:Real}(::AbstractArray{T<:Real,2}) at operators.jl:360
  • mysin{T<:Real}(::AbstractArray{T<:Real,N}) at operators.jl:362
  • mysin(x) at In[3]:2

Calling Python


In [13]:
using PyCall

In [ ]:
# you've got numpy
@pyimport numpy as np
x = [-100, 39, 59, 55, 20]
np.cumsum(x)

In [14]:
# you've got scipy
@pyimport scipy.optimize as so
function f(x)
    println("   calling f($x)")
    cos(x) - x
end
so.newton(f, 1.2)


   calling f(1.2)
   calling f(1.2002199999999998)
   calling f(0.7664554749111869)
   calling f(0.7412167885608414)
   calling f(0.7390978176492645)
   calling f(0.7390851391787693)
Out[14]:
0.7390851332151773

In [ ]:
# even matplotlib
using PyPlot

In [ ]:
x = linspace(0,2π,1000)
fig = plot(x, sin(3x + cos(5x)), "b--")
title("a funny plot")
fig = PyPlot.gcf()

Macros ("metaprogramming")

Macros are functions operate on expressions (code) rather than values: whereas a function takes input value(s), say 3 and returns some output value, say 9, a macro takes input expression(s), say x and returns an output expression, say x^2.

One might also say that macros rewrite or generate code.

Here is an example of why we might want to do this:


In [1]:
x = rand(5)

# suppose we want to time an element-wise square
t1 = time_ns()
x.^2
t2 = time_ns()
println(t2-t1, " nanoseconds")


8666325 nanoseconds

In [ ]:
@time x.^2

In [ ]:
macroexpand(:(@time x.^2))