You can edit almost every page by Creating an account. Otherwise, see the FAQ.

Phix (programming language)

From EverybodyWiki Bios & Wiki






Phix
Phix logo
ParadigmImperative, procedural, object-oriented
DeveloperPete Lomax
First appeared2006; 18 years ago (2006)
Stable release
0.8.2 / November 24, 2020; 3 years ago (2020-11-24)
Typing disciplinestatic, dynamic, strong, duck
Implementation languageself-hosted
OSWindows, Linux, Browser
LicenseOpen Software License 3.0 / Academic Free License 3.0
Filename extensions.e, .ex, .exw, .edb
Websitephix.x10.mx
Influenced by
Euphoria

Search Phix (programming language) on Amazon.

Phix is the open source, self-hosted, interpreted and compiled programming language that emphasizes simplicity and programmer friendly error messages.

History[edit]

Phix was released in 2006 based on the Euphoria programming language written by Robert Craig in 1993.

In 2013 Phix was re-written to replace the closed-source FASM back-end with open-source inline assembly.

Overview[edit]

Phix.[1] is designed to be simple, clear, and generic. The goal of Phix is to make writing programs, and then debugging easier.

Phix is a hybrid [2] interpreter/compiler:

  • As an interpreter:
    • p hello.exw
    • Phix reads, parses, makes executable code, and then executes it (directly from memory).
  • As a compiler:
    • p -c hello.exw
    • Duplicates interpreting, and saves the executable code to a file.
    • You can run the saved program at any time (and ship it, without need for Phix anymore).

The is no extra step of a virtual machine interpreting bytecode as with Python. While compiling, some additional optimizations may be found, so a compiled program may run faster than an interpreted program.

Phix extends the Euphoria tradition [3] of "fast to read, fast to pickup and fast to run".

Design features[edit]

For new programmers:

  • Available for Windows and Linux, in both 32 and 64 bit versions, and can be transpiled to JavaScript and run in a web browser.
  • The Windows installation includes a complete graphical user interface pGUI [4][5]out of the box.
  • The compiler generates executable binary files directly without the need to install any other compiler or linker; re-compiles itself by typing p -cp.
  • Compiler and run-time errors are made as human-readable as possible, and always include the offending source file name and line number.
  • The five primitive data types.
  • Explicitly tagged ends, such as if ... then ... end if catch mores errors and avoids problems like the dangling else.
  • Strings are fully mutable with variable length slice substitution. Sequences can grow and shrink at will with no manual housekeeping.
  • Consistent operators such that & operator always concatenates, and the + operator always adds unlike JavaScript[6][circular reference]
  • Lots of examples: PCAN[7]online archive, 500 bundled demos, and over 1250 entries on Rosettacode [8]

For experienced programmers:

  • Automatic garbage collection; available even for manually allocated raw memory
  • Run-time checking for out-of-bounds subscripts, uninitialized variables, inappropriate types.
  • A debugger provides single-stepping and the ability to enable/disable on selected blocks/files
  • Execution profiling
  • The switch statement requires an explict fallthrough statement which avoids need for continuous break statements
  • A simple built-in database [9]and SQlite [10], [11] wrappers.
  • Components such as ipc[12], json [13], curl [14], zip [15],gmp [16], regular expressions [17], sockets [18], and unit testing [19]

Types[edit]

Phix only needs five primitive data types:

            ┌────────┐
          ┌─┤ object ├─┐        ✼ Five primitive data-types
          │ └────────┘ │
          │            │
        number     sequence
          │            │
        integer      string

      └──────────┬─────────┘
                 │              ✼ Write your own ''type''
                type              to limit permitted values

    └────────────┬───────────┘
              struct            ✼ Reference data-types
              class               provide dot-notation and OOP

Phix has a novel view of data-types; [20] you can do anything with just five data-types. All values are objects, "a constituent that is acted upon," while a data-type "limits permitted values." That makes a Phix object the unlimited data-type.

There are only two distinct data-types you must learn: number and sequence.

  • An object is any value: number or sequence
  • A number is an integer or a floating point numeric value
    • An integer is a whole number (in the range ±1,000,000,000 )
    • ( An integer is also a number )
  • A sequence is a list of objects, of any length, and of any nesting level.
    • A string corresponds to UTF8 encoding; a list of character or byte values
    • ( A string is also a sequence )

A type is a user defined data-type. It is used to declare variables and for type-checking values as programs execute. Because it is programmable, you can add extra features beyond type-checking. For example, you could issue user created error messages.

The reference data-types, struct and {mono|class}}, deliver dot notation and OOP behavior.

The complexity of memory allocation and de-allocation is hidden and handled automatically by reference counting. The programmer only sees that data-types appear to be simple.

Indexing[edit]

The sequence is fundamental to the simplicity of Phix. Phix uses 1-based indexing because:

  • the first item is indexed is 1
  • the last index n is equal to the length of the sequence
  • numbering from the head 1 ... n is symmetrical to numbering from the tail -n ... -1
  • when searching for an item, an index value of 0 means "item not found"
  • a slice is inclusive from the first index to the last index
  • there are no off-by-one complications as found with 0-based indexing

Sequences with mixed values and strings work the same; both sequences and strings are mutable.

There have been many debates [21] [22] [23] [24] about the use of 0-based compared to 1-based indexing. Pointer based languages, and pointer arithmetic, as described by [25] by Edsger W. Dijkstra, have their place. Phix has no pointers and has no need for 0-based indexing.

-- is a line comment

// is also a line comment

//              1   2   3   4   5    -- index head to tail

sequence s = { 10, 20, 30, 40, 50 }

//             -5  -4  -3  -2  -1    -- index tail to head

// one item
    ? s[2]
    ? s[-4]
                -- output for both is:
    ----->  30

// slice of one item
    ? s[2..2]
    ? s[-4..-4]
                -- output for both is:
    -----> {30}

// inclusive slice
    ? s[2..4]
    ? s[-4..-2]
                -- output for both is:
    -----> {30,40,50}

// empty sequence
    ? s[3..2]
    ? s[-3..-4]
                -- output for both is:
    -----> {}

// insert
    s[3..2] = {99}
    ? s
    -----> {10,20,99,30,40,50}

// prepend and append
    s = { 10,20,30,40,50 }

    s[1..0] = {0}               -- prepend
    s[$+1..$] = {6}             -- append

    ? s
    -----> {0,10,20,99,30,40,50,6}

    s[0..-1] = {9999}           -- append

    ? s
    -----> {0,10,20,99,30,40,50,6,9999}

// delete
    s = { 10,20,30,40,50 }

    s[2..2] = {}        -- item deleted
    ? s
    -----> {10,30,40,50}

    s[2..3] = {}        -- slice deleted
    ? s
    -----> {10,50}

The virtual space between items 2 and3 is indexed as s[3..2]; this is where you can insert one item, or splice (concatenate) a sequence. Assigning a slice to an empty sequence {} deletes that slice. Use $ as a shorthand for length(s). You may now use s[1..0] to prepend a value to the head of sequence, and s[$+1..$] or even s[0..-1] to append a value to the tail of a sequence.

In "What’s the big deal? 0 vs 1 based indexing" [26] Steven Sagaert provides a simple explanation (for Julia):

That’s exactly it. 1-based indexing is actual indexing like in mathematics, while 0-based “indexing” isn’t indexing at all but pointer arithmetic. This comes from C where an array is just syntactic sugar for a pointer.

One-based indexing provides: intuitive and comprehensive operations [1] not possible with 0-based indexing), and that work identically for sequences and strings.

Parameter passing[edit]

All arguments are used as if they are passed as pass-by-value. An integer is passed by value, whereas number, sequence, and string are passed by reference with copy-on-write semantics. This gives the performance benefits of pass-by-reference but with the simple behavior of pass-by-value.

function set55(sequence s)
   s[5][5] = 5
   return s
end function

sequence a,b

// Case I
a = repeat(repeat(0,5),5) -- a 5x5 matrix
? a
    --> {{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0}}

// Case II
b = a
? b
    --> {{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0}}

// Case III
b = set55(a)
? b
    --> {{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,5}}

In Case I, sequence a is item {0,0,0,0,0} and four pointers to this item. No un-needed duplication occurs.

In Case II, sequence b is a pointer to a. No copying occurs.

In Case III, sequence a is unaltered. The unchanged items of b (that is b[1..4]) still point to a and only b[5] is actually changed.

Here a and b end up looking quite different but in fact still share 80% of their (lowest-level) contents.

Automatic PBR optimization[edit]

It is idiomatic use a sequence as an argument to a function and then immediately assign the result back to that sequence:

function chop( sequence a )
    for i=1 to length(a) do
        a[i] = truncate(a[i])
    end for
    return a
end function

sequence a = { 1.1, 2.2, 3.3, 4.4, 5.5 }
a = chop(a)
? a
    --> { 1,2,3,4,5}

Here the compiler applies a special optimization to make the local variable undefined over the call, leaving the parameter with a reference count of 1 and therefore amenable to in-situ modification.

In general, when parameters are modified locally (within the routine), sequences and sub-sequences are implemented very efficiently, because of copy-on-write semantics.

Reference types[edit]

In contrast dictionary, struct, and class instance parameters are more accurately described as pass-by-reference, similar to Java [27]

class mytest
  public string s
end class

procedure change(mytest c, d)
  c = new({"c"})
  d.s = "d"
end procedure

mytest
    a = new({"a"}),
    b = new({"b"})
change(a,b)

? a.s --> "a"
? b.s --> "d"
  • The parameter c points to the argument a. The assignment of c to new(), disconnects it from the argument, making c an independent variable. Changes to c are not visible in a.
  • The parameter d points to the argument b; changes to d are visible in b.

The statement delete(c) within the change procedure would cause the destruction of c, which in turn, would cause a.s to be destroyed. References to a.s would then crash Phix.

Examples[edit]

Line comments start with a double hyphen -- or a C-style // and continue to the end of line.

Block comments start with /* and finish with */.

Hello world, console[edit]

puts(1, "Hello, World!\n")

Hello world, gui[edit]

include pGUI.e
IupOpen()
IupShow(IupDialog(IupVbox({IupLabel("World!")},"MARGIN=90x20"),"TITLE=Hello"))
if platform()!=JS then
  IupMainLoop()
  IupClose()
end if

Simple function[edit]

include pGUI.e  -- for CD_DEG2RAD

function deg_to_rad(number deg)
    return deg*CD_DEG2RAD
end function
?deg_to_rad(180)        -- outputs 3.141592654
{} = deg_to_rad(360)    -- explict discard rqd

User defined types[edit]

type index(object x)
    return integer(x) and x>0
end type
index ndx = 5
ndx = -2    -- run-time error "type check failure, ndx is -2", plus source code file name and line number

User defined types are used primarily for validation and debugging purposes, rather than being fundamentally different to the five core builtin types.

String mutation[edit]

Strings and sequences have the same behavior. Strings are fully mutable, with variable length slice substitution:

string s = "food"  ?s   --> outputs "food"
s[2..3] = "e"      ?s   --> outputs "fed"
s[2..1] = "east"   ?s   --> outputs "feasted"

Exception handling[edit]

try
   integer i = 1/0
   -- or, for example, throw("file in use")
catch e
   ?e[E_USER]
end try
puts(1,"still running...\n")

Output:

 "attempt to divide by 0" -- or "file in use"
 still running...

Filtering[edit]

function odd(integer a) return remainder(a,2)=1 end function
function even(integer a) return remainder(a,2)=0 end function

?filter(tagset(10),odd)     -- ==> {1,3,5,7,9}
?filter(tagset(10),even)    -- ==> {2,4,6,8,10}

Version control[edit]

requires("0.8.2")           -- crashes on 0.8.1 and earlier
requires(WINDOWS)           -- crashes on Linux
requires(64)                -- crashes on 32-bit

Specify what versions and/or operating systems are required to run the source code:

In the latter case, if you try to run a 64-bit-only program with a 32-bit interpreter, it will try to locate a suitable 64-bit interpreter and offer to re-run it with that (and vice-versa, for instance the older arwen and win32lib libraries are 32-bit only).

Unit testing[edit]

test_equal(2+2,4,"2+2 is not 4 ====")
test_summary()

If all goes well, no output is shown, and the program carries on normally.

You can easily force a summary to be output, quietly create a logfile, etc. [28]

Error reporting[edit]

When a Phix program crashes, it produces a programmer-readable file,ex.err, which contains the full call stack and the value of every variable at that point. These can be quite large, but the most pertinent information is typically at the start of the file.

Error messages are made as clear as possible, for example

  C:\Program Files (x86)\Phix\demo\ilest.exw:43 in function strip_comments()
  type check failure, line is {"--","-- builtins/assert.e (an autoinclude)..

At that particular point line was supposed to be a string, not a list of strings.

Where possible the compiler tries to pre-empt that kind of run-time error with a compile-time error:

  C:\Program Files (x86)\Phix\demo\ilest.exw:43 in function strip_comments()
  line = 5
       ^ type error (storing atom in string)

Source level tracing[edit]

Place with trace before any block of code you want to step through, and without trace before any block you want to skip

The program will then run until the condition (}}i=1234}}) is met, before single-stepping through the subsequent code.

Source level tracing in Phix

Type based debugging[edit]

Suppose some tablet has the contents {12.35, 15.87, 17.17, ..} at some point of failure, but you were expecting t[3] to be 17.57.

It would normally be very helpful to know where exactly the wrong contents were placed in that table. Edit and re-run with say:

--sequence t = {}
type badtable(sequence t)
   if length(t)>=3 and t[3]<17.2 then
      ?9/0
   end if
   return true
end type
badtable t = {}

That will crash at the required point, producing an ex.err, alternatively you could trace(1) to start source-level tracing instead.

Note that without typecheck directives in the source code can completely disable this technique.

Feature summary[edit]

Paradigms: imperative, procedural, optionally object-oriented

Standardized: No, the manual includes the language specification

Type strength: strong

Type safety: safe

Expression of types: explicit, partially implicit

Type compatibility: duck

Type checking: dynamic, static

Parameter Passing Methods Available: copy on write, immutable reference, multiple returns

Garbage collection: Reference counting

Intended use: Application, Educational, General, High-level scripting, Text processing

Design goals: Simplicity, Readability, Ease of use

Unsupported features[edit]

Phix does not (although most can be emulated) directly support operator/builtin/function overloading, lambda expressions, closures, currying, eval, partial function application, function composition, function prototyping, monads, generators, anonymous recursion, the Y combinator, aspect oriented programming, interfaces, delegates, first class environments, implicit type conversion (of the destructive kind), interactive programming, inverted syntax, list comprehensions, metaprogramming, pointers (other than to raw allocated memory), topic variables, enforced singletons, safe mode, s-expressions, or formal proof construction. The author wryly comments "That should both scare off and attract the right people"

No operator overloading means that + always adds values.

No function overloading refers to there being at most one (polymorphic) version of a routine in a given scope

A simple and compact language is not limiting. Phix has some 1,277 [29] completed Rosettacode [30] tasks (second [31] only to Go).

Criticism[edit]

The inline assembly on which Phix is based is x86/64 only, making an ARM port extremely difficult.

The extensive run-time checking, which can increase productivity, incurs an inevitable performance penalty (at times a factor of 8), Type-checking can be reduced with the without typecheck statement, but not eliminated completely.

Floating point operations typically incur additional indirection and/or memory allocation compared to other languages.

The use of inline assembly to alleviate runtime hotspots is both difficult to understand and poorly documented.

Comparable languages[edit]

External links[edit]

Languages implemented in Phix[edit]

References[edit]

The Phix Programming Language[edit]


This article "Phix (programming language)" is from Wikipedia. The list of its authors can be seen in its historical and/or the page Edithistory:Phix (programming language). Articles copied from Draft Namespace on Wikipedia could be seen on the Draft Namespace of Wikipedia and not main one.

  1. "Phix". Retrieved 2021-01-10.
  2. "Phix docs hybrid". Retrieved 2021-01-10.
  3. "LinuxFormat" (PDF). Retrieved 2021-01-10.
  4. "pGUI documentation". Retrieved 2020-12-31.
  5. "IUP". Retrieved 2020-12-31.
  6. "Javascript + operator". Retrieved 2021-01-09.
  7. "PCAN". Retrieved 2021-01-16.
  8. "Phix on Rosetta Code". Retrieved 2021-01-14.
  9. "database documentation". Retrieved 2020-12-31.
  10. "SQLite". Retrieved 2021-01-14.
  11. "pSQLite documentation". Retrieved 2021-01-14.
  12. "ipc documentation". Retrieved 2021-01-14.
  13. "json documentation". Retrieved 2021-01-14.
  14. "libcurl documentation". Retrieved 2021-01-14.
  15. "LiteZip documentation". Retrieved 2021-01-14.
  16. "gmp/mpfr documentation". Retrieved 2021-01-14.
  17. "regex documentation". Retrieved 2021-01-14.
  18. "sockets documentation". Retrieved 2021-01-14.
  19. "unit test documentation". Retrieved 2021-01-14.
  20. "Phix docs". Retrieved 2021-01-10.
  21. "Is Index Origin 0 a Hindrance? Roger Hui". Retrieved 2021-01-09.
  22. "Thread on Julia google groups". Retrieved 2021-01-09.
  23. "Again on 0-based vs. 1-based indexing". Retrieved 2021-01-20.
  24. "Lua, a misunderstood language". Retrieved 2021-01-20.
  25. "Indexing of Arrays: 0 vs 1". Retrieved 2021-01-17.
  26. "What's the big deal? 0 vs 1 based indexing". Retrieved 2021-01-14.
  27. "Java is Pass-by-Value, Dammit!". Retrieved 2021-01-17.
  28. "unit test documentation". Retrieved 2021-01-14.
  29. "Phix on Rosetta Code". Retrieved 2021-01-14.
  30. "Rosetta Code". Retrieved 2021-01-14.
  31. "Rosetta Code/Count examples/Full list". Retrieved 2021-01-14.