Compile-time calculation: Difference between revisions
m Added Erlang |
added Julia example |
||
Line 377: | Line 377: | ||
<lang J> pf10 '' |
<lang J> pf10 '' |
||
3628800</lang> |
3628800</lang> |
||
=={{header|Julia}}== |
|||
Julia includes a powerful macro feature that can perform arbitrary code transformations at compile-time (or technically at parse-time), and can also execute arbitrary Julia code. For example, the following macro computes the factorial of <code>n</code> (a literal constant) and returns the value (e.g. to inline it in the resulting source code) |
|||
<lang julia>macro fact(n) |
|||
factorial(n) |
|||
end</lang> |
|||
If we now use this in a function, e.g. |
|||
<lang julia>foo() = @fact 10</lang> |
|||
then the value of 10! = 3628800 is computed at parse-time and is inlined in the compiled function <code>foo</code>, as can be verified by inspecting the assembly code via the built-in function <code>code_native(foo, ())</code>. |
|||
=={{header|m4}}== |
=={{header|m4}}== |
Revision as of 16:03, 11 November 2013
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
Some programming languages allow calculation of values at compile time. For this task, calculate 10! at compile time. Print the result when the program is run.
Discuss what limitations apply to compile-time calculations in your language.
Ada
Here's a hardcoded version: <lang ada> with Ada.Text_Io; procedure CompileTimeCalculation is
Factorial : constant Integer := 10*9*8*7*6*5*4*3*2*1;
begin
Ada.Text_Io.Put(Integer'Image(Factorial));
end CompileTimeCalculation; </lang> And here's a recursive function version that prints the exact same thing. <lang ada> with Ada.Text_Io; procedure CompileTimeCalculation is
function Factorial (Int : in Integer) return Integer is begin if Int > 1 then return Int * Factorial(Int-1); else return 1; end if; end; Fact10 : Integer := Factorial(10);
begin
Ada.Text_Io.Put(Integer'Image(Fact10));
end CompileTimeCalculation;</lang>
Unbounded Compile-Time Calculation
An interesting property of Ada is that such calculations at compile time are performed with mathematical (i.e., unbounded) integers for intermediate results. On a compiler with 32-bit integers (gcc), the following code prints the value of '20 choose 10' = 184756:
<lang Ada>with Ada.Text_IO;
procedure Unbounded_Compile_Time_Calculation is
F_10 : constant Integer := 10*9*8*7*6*5*4*3*2*1; A_11_15 : constant Integer := 15*14*13*12*11; A_16_20 : constant Integer := 20*19*18*17*16;
begin
Ada.Text_IO.Put_Line -- prints out ("20 choose 10 =" & Integer'Image((A_11_15 * A_16_20 * F_10) / (F_10 * F_10)));
-- Ada.Text_IO.Put_Line -- would not compile -- ("Factorial(20) =" & Integer'Image(A_11_15 * A_16_20 * F_10)); end Unbounded_Compile_Time_Calculation;</lang>
The same compiler refuses to compile the two two lines
<lang Ada> Ada.Text_IO.Put_Line -- would not compile
("Factorial(20) =" & Integer'Image(A_11_15 * A_16_20 * F_10));</lang>
because the final result A_11_15 * A_16_20 * F_10 is a value not in range of type "Standard.Integer" -- the same intermediate value that was used above to compute '20 choose 10'.
BASIC
Most BASICs perform compile-time calculation on anything they can determine is a constant. This can either be done explicitly: <lang qbasic>CONST factorial10 = 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10</lang>
or implicitly:
<lang qbasic>DIM factorial10 AS LONG factorial10 = 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10</lang>
In both cases, the identifier factorial10
is given the value 3628800 without any runtime calculations, although in many (or perhaps most) BASICs the first one is handled similarly to C's #define
: if it isn't used elsewhere in the code, it doesn't appear at all in the final executable.
C
C includes a macro processor that runs at compile-time. With the use of suitably imaginative macro library includes, it can be used in a similar way to C++ template metaprogramming or Lisp macros. Like C++, and unlike Lisp, the language used is usually very different from the C language used to write runtime code.
The Order macro library implements a full virtual machine and high-level, functional programming language available to C programs at compile-time: <lang c>#include <stdio.h>
- include <order/interpreter.h>
- define ORDER_PP_DEF_8fac ORDER_PP_FN( \
8fn(8X, 8seq_fold(8times, 1, 8seq_iota(1, 8inc(8X)))) )
int main(void) { printf("10! = %d\n", ORDER_PP( 8to_lit( 8fac(10) ) ) ); return 0; }</lang>
In this example, the 8fac
function computes the factorial by folding 8times
(the binary multiplication primitive) over a numeric range created by 8seq_iota
(which requires 8X
to be incremented, as it creates lists from L to R-1), a familiar way of computing this in functional languages. The result of the factorial calculation is an internal, "native" number which need to be converted to decimal by the 8to_lit
function.
If the compiler allows, run the preprocessor only (the -E
option with GCC) to see the result in place.
- Output:
3628800
This and similar macro libraries are very demanding on the preprocessor, and will require a standards-compliant implementation such as GCC.
C++
This is called Template metaprogramming. In fact, templates in C++ are Turing-complete, making deciding whether a program will compile undecidable. <lang cpp>#include <iostream>
template<int i> struct Fac {
static const int result = i * Fac<i-1>::result;
};
template<> struct Fac<1> {
static const int result = 1;
};
int main()
{
std::cout << "10! = " << Fac<10>::result << "\n"; return 0;
}</lang>
Compile-time calculations in C++ look quite different from normal code. We can only use templates, type definitions and a subset of integer arithmetic. It is not possible to use iteration. C++ compile-time programs are similar to programs in pure functional programming languages, albeit with a peculiar syntax.
Alternative version, using constexpr in C++11:
<lang cpp>#include <stdio.h>
constexpr int factorial(int n) {
return n ? (n * factorial(n - 1)) : 1;
}
constexpr int f10 = factorial(10);
int main() {
printf("%d\n", f10); return 0;
}</lang> Output:
3628800
The asm produced by G++ 4.6.0 32 bit (-std=c++0x -S), shows the computation is done at compile-time: <lang asm>_main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $16, %esp call ___main movl $3628800, 4(%esp) movl $LC0, (%esp) call _printf movl $0, %eax leave ret</lang>
Clojure
<lang clojure> (defn fac [n] (apply * (range 1 (inc n)))) (defmacro ct-factorial [n] (fac n))</lang>
Common Lisp
Assuming a definition from Factorial function#Common Lisp, we first have to make a small adjustment so that the function is available at compile time. Common Lisp does not have a single image building and deployment model. For instance, Common Lisp implementations can support a "C like" model whereby a compiler is invoked as a separate process to handle individual files, which are then loaded to form an image (analogous to linking). A Lisp compiler will not make available to itself the functions in a source file which it happens to be compiling, unless told to do so:
<lang lisp>(eval-when (:compile-toplevel :load-toplevel :execute)
(defun factorial ...))</lang>
With that, here are ways to do compile-time evaluation:
<lang lisp>(defmacro ct-factorial (n)
(factorial n))
...
(print (ct-factorial 10))</lang>
The factorial
function must be defined before any use of the ct-factorial
macro is evaluated or compiled.
If the data resulting from the compile-time calculation is not necessarily a number or other self-evaluating object, as it is in the factorial case, then the macro must quote it to avoid it being interpreted as code (a form):
<lang lisp>(defmacro ct-factorial (n)
`(quote ,(factorial n)))
- or, equivalently,
(defmacro ct-factorial (n)
`',(factorial n))</lang>
It is also possible to have a value computed at load time, when the code is loaded into the process, rather than at compile time; this is useful if the value to be computed contains objects that do not yet exist at compile time, or the value might vary due to properties which might be different while yet using the same compiled program (e.g. pathnames), but it is still constant for one execution of the program:
<lang lisp>(print (load-time-value (factorial 10)))</lang>
Further it's also possible to have the value computed at read time using the read macro #.
.
<lang lisp>(print (#. (factorial 10)))</lang>
Lastly, Common Lisp has "compiler macros" which are user-defined handlers for function call optimization. A compiler macro is defined which has the same name as some user-defined function. When calls to that function are being compiled, they pass through the macro. The macro must analyze the arguments and rewrite the function call into something else, or return the original form.
<lang lisp>(define-compiler-macro factorial (&whole form arg)
(if (constantp arg) (factorial arg) form))</lang>
Test with CLISP (taking advantage of its !
function) showing how a factorial call with a constant argument of 10 ends up compiled to the constant 3268800, but a factorial call with the argument a is compiled to a variable access and function call:
[1]> (defun factorial (x) (! x)) FACTORIAL [2]> (define-compiler-macro factorial (&whole form arg) (if (constantp arg) (factorial arg) form)) FACTORIAL [3]> (defun test-constant () (factorial 10)) TEST-CONSTANT [4]> (disassemble 'test-constant) Disassembly of function TEST-CONSTANT (CONST 0) = 3628800 [ .. snip ... ] 0 (CONST 0) ; 3628800 1 (SKIP&RET 1) NIL [5]> (defun test-nonconstant () (factorial a)) TEST-NONCONSTANT [6]> (disassemble 'test-nonconstant) WARNING in TEST-NONCONSTANT : A is neither declared nor bound, it will be treated as if it were declared SPECIAL. Disassembly of function TEST-NONCONSTANT (CONST 0) = A (CONST 1) = FACTORIAL [ .. snip ... ] reads special variable: A 3 byte-code instructions: 0 (GETVALUE&PUSH 0) ; A 2 (CALL1 1) ; FACTORIAL 4 (SKIP&RET 1) NIL
D
The D compiler is able to run many functions at compile-time Compile Time Function Execution (CTFE): <lang d>import core.stdc.stdio;
long fact(long x) {
long result = 1; foreach (i; 2 .. x + 1) result *= i; return result;
}
void main() {
// enum means "compile-time constant", it forces CTFE. enum fact10 = fact(10);
printf("%ld\n", fact10);
}</lang>
The 32-bit asm generated by DMD shows the computation is done at compile-time: <lang asm>__Dmain
push EAX mov EAX,offset FLAT:_DATA push 0 push 0375F00h push EAX call near ptr _printf add ESP,0Ch xor EAX,EAX pop ECX ret</lang>
Delphi
- See Pascal
DWScript
In DWScript, constant expressions and referentially-transparent built-in functions, such as Factorial, are evaluated at compile time.
<lang delphi>const fact10 = Factorial(10);</lang>
Erlang
This is a placeholder since to do something more complex than text substitution macros Erlang offers parse transformations. This is a quote from their documentation: "Programmers are strongly advised not to engage in parse transformations". Somebody can do this task, but not I.
Factor
Technically, this calculation happens at parse-time, before any compilation takes place. Calculating factorial at compile-time is not useful in Factor.
<lang factor>: factorial ( n -- n! ) [1,b] product ;
CONSTANT: 10-factorial $[ 10 factorial ]</lang>
Forth
During a word definition, you can drop out of the compilation state with [ and go back in with ]. (This is where the naming conventions for [CHAR] and ['] come from.) There are several flavors of LITERAL for compiling the result into the word.
<lang forth>: fac ( n -- n! ) 1 swap 1+ 2 max 2 ?do i * loop ;
- main ." 10! = " [ 10 fac ] literal . ;
see main
- main
.\" 10! = " 3628800 . ; ok</lang>
Outside of a word definition, it's fuzzy. If the following code is itself followed by a test and output, and is run in a script, then the construction of the bignum array (and the perhaps native-code compilation of more) happens at runtime. If the following code is followed by a command that creates an executable, the array will not be rebuilt on each run.
<lang forth>
- more ( "digits" -- ) \ store "1234" as 1 c, 2 c, 3 c, 4 c,
parse-word bounds ?do i c@ [char] 0 - c, loop ;
create bignum more 73167176531330624919225119674426574742355349194934 more 96983520312774506326239578318016984801869478851843 ...</lang>
Fortran
In Fortran, parameters can be defined where the value is computed at compile time: <lang fortran> program test
implicit none integer,parameter :: t = 10*9*8*7*6*5*4*3*2 !computed at compile time
write(*,*) t !write the value the console.
end program test
</lang>
Go
Constant expressions are evaluated at compile time. A constant expression though, is pretty simple and can't have much more than literals, operators, and a special thing called iota. There is no way to loop in a constant expression and so the expanded expression below is about the simplest way of completing this task. <lang go>package main
import "fmt"
func main() {
fmt.Println(2*3*4*5*6*7*8*9*10)
}</lang>
Haskell
With Template Haskell and Quasiquotes, it is quite easy to do compile time embedding.
<lang haskell>{-# LANGUAGE QuasiQuotes, TemplateHaskell #-}
fact n = product [1..n]
main = print $([|fact 10|])</lang>
J
J is an interpreter, and not a compiler, so could be said to not have any "compile time". Nevertheless, J tacit programs are stored using an internal representation -- the program is parsed once, well before it is used.
Thus, a program which prints 10 factorial:
<lang J>pf10=: smoutput bind (!10)</lang>
When the definition of pf10 is examined, it contains the value 3628800. J has several ways of representing tacit programs. Here all five of them are presented for this program (the last two happen to look identical for this trivial case):
<lang J> 9!:3]1 2 4 5 6
pf10
┌───────────────────────────────────────┐ │┌─┬───────────────────────────────────┐│ ││@│┌────────┬────────────────────────┐││ ││ ││smoutput│┌─┬────────────────────┐│││ ││ ││ ││"│┌────────────┬─────┐││││ ││ ││ ││ ││┌─┬────────┐│┌─┬─┐│││││ ││ ││ ││ │││0│3.6288e6│││0│_││││││ ││ ││ ││ ││└─┴────────┘│└─┴─┘│││││ ││ ││ ││ │└────────────┴─────┘││││ ││ ││ │└─┴────────────────────┘│││ ││ │└────────┴────────────────────────┘││ │└─┴───────────────────────────────────┘│ └───────────────────────────────────────┘ ┌────────┬─┬──────────────┐ │smoutput│@│┌────────┬─┬─┐│ │ │ ││3.6288e6│"│_││ │ │ │└────────┴─┴─┘│ └────────┴─┴──────────────┘
┌─ smoutput
── @ ─┤ ┌─ 3628800
└─ " ──────┴─ _
smoutput@(3628800"_) smoutput@(3628800"_)</lang>
Finally, when this program is run, it displays this number:
<lang J> pf10 3628800</lang>
Julia
Julia includes a powerful macro feature that can perform arbitrary code transformations at compile-time (or technically at parse-time), and can also execute arbitrary Julia code. For example, the following macro computes the factorial of n
(a literal constant) and returns the value (e.g. to inline it in the resulting source code)
<lang julia>macro fact(n)
factorial(n)
end</lang>
If we now use this in a function, e.g.
<lang julia>foo() = @fact 10</lang>
then the value of 10! = 3628800 is computed at parse-time and is inlined in the compiled function foo
, as can be verified by inspecting the assembly code via the built-in function code_native(foo, ())
.
m4
m4 expands macros at run time, not compile time. If m4 is a front end to some other langugage, then m4's run time is part of other language's compile time.
This example uses m4 as a front end to AWK. m4 calculates factorial of 10, where AWK program calls macro.
<lang m4>define(`factorial', `ifelse($1, 0, 1, `eval($1 * factorial(eval($1 - 1)))')')dnl dnl BEGIN { print "10! is factorial(10)" }</lang>
One runs m4 program.m4 > program.awk
to make this valid AWK program.
<lang awk>BEGIN { print "10! is 3628800" }</lang>
Mathematica
Mathematica is not a compiled language, you can construct compiled functions in Mathematica by the build-in function "Compile". Constants are calculated at "compile-time". <lang>f = Compile[{}, 10!]</lang>
- Output:
CompiledFunction[{},3628800,-CompiledCode-]
<lang>f[]</lang>
- Output:
3628800
Nimrod
Nimrod can evaluate procedures at compile-time, this can be forced by calling a procedure with a const keyword like so:
<lang Nimrod>proc fact(x: int): int =
result = 1 for i in 2..x: result = result * i
const fact10 = fact(10) echo(fact10)</lang>
We can see that this is evaluated at compile-time by looking at the generated C code:
<lang C>... STRING_LITERAL(TMP122, "3628800", 7); ...</lang>
The Nimrod compiler can also be told to try to evaluate procedures at compile-time even for variables by using the --implicitStatic:on command line switch. The Nimrod compiler performs a side effect analysis to make sure that the procedure is side effect free, if it is not; a compile-time error is raised.
Objeck
Objeck will fold constants at compiler time as long as the -s2 or -s3 compiler switches are enabled.
<lang objeck> bundle Default {
class CompileTime { function : Main(args : String[]) ~ Nil { (10*9*8*7*6*5*4*3*2*1)->PrintLine(); } }
} </lang>
OCaml
OCaml does not calculate operations that involve functions calls, as for example factorial 10, but OCaml does calculate simple mathematical operations at compile-time, for example in the code below (24 * 60 * 60)
will be replaced by its result 86400
.
<lang ocaml>let days_to_seconds n =
let conv = 24 * 60 * 60 in (n * conv)
- </lang>
It is easy to verify this using the argument -S
to keep the intermediate assembly file:
ocamlopt -S sec.ml
grep 86400 sec.s
imull $86400, %eax
If you wish to verify this property in your own projects, you have to know that integer values most often have their OCaml internal representation in the assembly, which for an integer x
its OCaml internal representation will be (((x) << 1) + 1)
. So for example if we modify the previous code for:
<lang ocaml>let conv = 24 * 60 * 60
let days_to_seconds n =
(n * conv)
- </lang>
# (24 * 60 * 60) lsl 1 + 1 ;; - : int = 172801
grep 172801 sec.s
movl $172801, camlSec
OxygenBasic
To demonstrate compiler timing, A custom compiler is created here with the system performance counter to measure lapsed time. The source code is embedded for brevity.
To dimension a static array, the macro Pling10 is resolved at compile time. The overall compile time (ready to execute) was around 23 milliseconds.
<lang oxygenbasic> 'LIBRARY CALLS '=============
extern lib "../../oxygen.dll"
declare o2_basic (string src) declare o2_exec (optional sys p) as sys declare o2_errno () as sys declare o2_error () as string
extern lib "kernel32.dll"
declare QueryPerformanceFrequency(quad*freq) declare QueryPerformanceCounter(quad*count)
end extern
'EMBEDDED SOURCE CODE '====================
src=quote
Source
def Pling10 2*3*4*5*6*7*8*9*10
byte a[pling10] 'Pling10 is resolved to a number here at compile time
print pling10
Source
'TIMER '=====
quad ts,tc,freq QueryPerformanceFrequency freq QueryPerformanceCounter ts
'COMPILE/EXECUTE '===============
o2_basic src
if o2_errno then
print o2_error
else
QueryPerformanceCounter tc print "Compile time: " str((tc-ts)*1000/freq, 1) " MilliSeconds" o2_exec 'Run the program
end if </lang>
Oz
<lang oz>functor import
System Application
prepare
fun {Fac N} {FoldL {List.number 1 N 1} Number.'*' 1} end Fac10 = {Fac 10}
define
{System.showInfo "10! = "#Fac10} {Application.exit 0}
end</lang>
Code in the prepare
section of a functor is executed at compile time. External modules that are used in this code must be imported with a require
statement (not shown in this example). Such external functors must have been compiled before the current functor is compiled (ozmake
will automatically take care of this).
It is possible to export variables that are defined in the prepare
statement. However, such variables must not be stateful entities, e.g. it is not possible to export a cell that was defined at compile time.
Pascal
All the variants of pascal have always been able to calculate the values of constants at compile time as long as the values can be resolved.
<lang pascal> program in out;
const
X = 10*9*8*7*6*5*4*3*2*1 ;
begin
writeln(x);
end; </lang>
Perl
There are few limits on code you can put in BEGIN
blocks, which are executed at compile-time. Unfortunately, you can't in general save the compiled form of a program to run later. Instead, perl
recompiles your program every time you run it.
<lang perl>my $tenfactorial; print "$tenfactorial\n";
BEGIN
{$tenfactorial = 1; $tenfactorial *= $_ foreach 1 .. 10;}</lang>
Note however that all constant folding is done at compile time, so this actually does the factorial at compile time.
<lang perl>my $tenfactorial = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2;</lang>
Perl 6
<lang perl6>constant $tenfact = [*] 2..10; say $tenfact;</lang>
Like Perl 5, we also have a BEGIN block, but it also works to introduce a blockless statement, the value of which will be stored up to be used in the surrounding expression at run time:
<lang perl6> say(BEGIN [*] 2..10);</lang>
PicoLisp
The PicoLisp "compiler" is the so-called "reader", which converts the human-readable source code into nested internal pointer structures. When it runs, arbitrary expressions can be executed with the backqoute and tilde operators (read macros). <lang PicoLisp>(de fact (N)
(apply * (range 1 N)) )
(de foo ()
(prinl "The value of fact(10) is " `(fact 10)) )</lang>
Output:
: (pp 'foo) # Pretty-print the function (de foo NIL (prinl "The value of fact(10) is " 3628800) ) -> foo : (foo) # Execute it The value of fact(10) is 3628800 -> 3628800
PL/I
<lang PL/I> /* Factorials using the pre-processor. */ test: procedure options (main);
%factorial: procedure (N) returns (fixed);
declare N fixed; declare (i, k) fixed;
k = 1; do i = 2 to N; k = k*i; end; return (k);
%end factorial;
%activate factorial;
declare (x, y) fixed decimal; x = factorial (4); put ('factorial 4 is ', x); y = factorial (6); put skip list ('factorial 6 is ', y);
end test; </lang>
Output from the pre-processor:
<lang> /* Factorials using the pre-processor. */ test: procedure options (main);
declare (x, y) fixed decimal; x = 24; put ('factorial 4 is ', x); y = 720; put skip list ('factorial 6 is ', y);
end test; </lang>
Execution results:
<lang> factorial 4 is 24 factorial 6 is 720 </lang>
PureBasic
PureBasic will do most calculation during compiling, e.g. <lang PureBasic>a=1*2*3*4*5*6*7*8*9*10</lang> could on a x86 be complied to
MOV dword [v_a],3628800
Racket
Racket, like most Lisp descendants, allows arbitrary code to be executed at compile-time.
<lang racket>
- lang racket
- Import the math library for compile-time
- Note
- included in Racket v5.3.2
(require (for-syntax math))
- In versions older than v5.3.2, just define the function
- for compile-time
- (begin-for-syntax
- (define (factorial n)
- (if (zero? n)
- 1
- (factorial (- n 1)))))
- define a macro that calls factorial at compile-time
(define-syntax (fact10 stx)
#`#,(factorial 10))
- use the macro defined above
(fact10) </lang>
REXX
Since REXX is an interpreted language, run time is compile time. <lang rexx>/*REXX program to compute 10!*/ say '10! =' !(10); exit
!: procedure; !=1; do j=2 to arg(1); !=!*j; end; return !</lang> output
10! = 3628800
Seed7
Seed7 allows predefined and user defined initialisation expressions. The ! operator is predefined, so no user defined function is necessary.
<lang seed7>$ include "seed7_05.s7i";
const proc: main is func
local const integer: factorial is !10; begin writeln(factorial); end func;</lang>
Tcl
In Tcl, compilation happens dynamically when required rather than being a separate step. That said, it is possible to use the language's introspection engine to discover what code has been compiled to, making it easy to show that known-constant expressions are compiled to their results. Generating the expression to compile is then simple enough.
<lang tcl>proc makeFacExpr n {
set exp 1 for {set i 2} {$i <= $n} {incr i} { append exp " * $i" } return "expr \{$exp\}"
} eval [makeFacExpr 10]</lang> How to show that the results were compiled? Like this: <lang tcl>% tcl::unsupported::disassemble script [makeFacExpr 10] ByteCode 0x0x4de10, refCt 1, epoch 3, interp 0x0x31c10 (epoch 3)
Source "expr {1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10}" Cmds 1, src 45, inst 3, litObjs 1, aux 0, stkDepth 1, code/src 0.00 Commands 1: 1: pc 0-1, src 0-44 Command 1: "expr {1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10}" (0) push1 0 # "3628800" (2) done
</lang> As you can see, that expression was transformed into just a push of the results (and an instruction to mark the end of the bytecode segment).
Ursala
Any user-defined or library function callable at run time can also be called at compile time and evaluated with no unusual ceremony involved. <lang Ursala>#import nat
x = factorial 10
- executable&
comcal = ! (%nP x)--<></lang> some notes:
x
is declared as a constant equal to ten factorial using thefactorial
function imported from thenat
library.%nP
is a function derived from the type expression%n
, for natural numbers, which takes a natural number as an argument and maps it to a list of character strings suitable for printing- The
#executable&
directive causes the function following to be compiled as a free standing executable transforming standard input to standard output thereby. - The
--
operator represents list concatenation. - The list containing the empty string is concatenated with
(%nP x)
so that the output will be terminated with a line break. - The
!
operator makes a constant function of its operand, so that the compiled program will ignore its input and printx
regardless.
Here is a bash session showing compilation of the above code into a simple command line filter, and running it as the next command.
$ fun comcal.fun fun: writing `comcal' $ comcal < /dev/null 3628800
Similarly to the Ocaml and Tcl solutions, we can confirm that the calculation has been performed at compile time by inspecting the object code.
$ fun comcal --decompile main = constant <'3628800',''>
XPL0
<lang XPL0>code IntOut=11; IntOut(0, 10*9*8*7*6*5*4*3*2); </lang> Generates this 80386 assembly code:
XOR EAX,EAX PUSH EAX MOV EAX,3628800 CALL INTR11 RET
- Programming Tasks
- Solutions by Programming Task
- Ada
- BASIC
- BBC BASIC/Omit
- C
- C++
- Clojure
- Common Lisp
- D
- Delphi
- DWScript
- Erlang
- Factor
- Forth
- Fortran
- Go
- Haskell
- J
- Julia
- M4
- Mathematica
- Nimrod
- Objeck
- OCaml
- OxygenBasic
- Oz
- Pascal
- Perl
- Perl 6
- PicoLisp
- PL/I
- PureBasic
- Racket
- REXX
- Seed7
- Tcl
- Ursala
- XPL0
- AutoHotkey/Omit
- AWK/Omit
- Bc/Omit
- Brlcad/Omit
- C sharp/Omit
- E/Omit
- Gambas/Omit
- GUISS/Omit
- Icon/Omit
- Java/Omit
- JavaScript/Omit
- Locomotive Basic/Omit
- Lua/Omit
- NetRexx/Omit
- Openscad/Omit
- PARI/GP/Omit
- Python/Omit
- Ruby/Omit
- Unicon/Omit
- UNIX Shell/Omit
- ZX Spectrum Basic/Omit