mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-23 09:41:36 +00:00
Add phel.html
This commit is contained in:
parent
8240a8f703
commit
f6c3921900
333
phel.html.markdown
Normal file
333
phel.html.markdown
Normal file
@ -0,0 +1,333 @@
|
||||
---
|
||||
language: phel
|
||||
filename: learnphel.phel
|
||||
contributors:
|
||||
- ["Chemaclass", "https://github.com/Chemaclass"]
|
||||
---
|
||||
|
||||
[Phel](https://phel-lang.org/) is a functional programming language that compiles to PHP.
|
||||
It is a dialect of Lisp inspired by Phel and Janet.
|
||||
|
||||
## Features
|
||||
- Built on PHP's ecosystem
|
||||
- Good error reporting
|
||||
- Persistent Datastructures (Lists, Vectors, Maps and Sets)
|
||||
- Macros
|
||||
- Recursive functions
|
||||
- Powerful but simple Syntax
|
||||
- REPL
|
||||
|
||||
```phel
|
||||
# Comments begin with a # character and continue until the end of the line. There are no multi-line comments.
|
||||
|
||||
# Phel is written in "forms", which are just
|
||||
# lists of things inside parentheses, separated by whitespace.
|
||||
|
||||
# The first call in a file should be ns, to set the namespace
|
||||
(ns learn-phel)
|
||||
|
||||
# More basic examples:
|
||||
|
||||
# str will create a string out of all its arguments
|
||||
(str "Hello" " " "World") # => "Hello World"
|
||||
|
||||
# Math is straightforward
|
||||
(+ 1 1) # => 2
|
||||
(- 2 1) # => 1
|
||||
(* 1 2) # => 2
|
||||
(/ 2 1) # => 2
|
||||
|
||||
# Equality is =
|
||||
(= 1 1) # => true
|
||||
(= 2 1) # => false
|
||||
|
||||
# You need not for logic, too
|
||||
(not true) # => false
|
||||
|
||||
# Nesting forms works as you expect
|
||||
(+ 1 (- 3 2)) # = 1 + (3 - 2) => 2
|
||||
|
||||
# Phel inherits PHP under the hood, so it can use native PHP (functions and classes) without any additional cost
|
||||
# by using the `php/` prefix to all PHP native functions.
|
||||
|
||||
# Types
|
||||
#############
|
||||
|
||||
# Booleans are similar as the native PHP ones
|
||||
|
||||
nil
|
||||
true
|
||||
false
|
||||
|
||||
# Symbols are used to name functions and variables in Phel
|
||||
# For example: symbol, snake_case_symbol, my-module/my-function
|
||||
|
||||
# Keywords are like symbols that begin with a colon character. However, they are used as constants rather than a name for something.
|
||||
|
||||
:keyword
|
||||
:0x0x0x
|
||||
::
|
||||
|
||||
# Numbers in Phel are equivalent to numbers in PHP
|
||||
|
||||
1337 # integer
|
||||
+1337 # positive integer
|
||||
-1337 # negative integer
|
||||
|
||||
1.234 # float
|
||||
+1.234 # positive float
|
||||
-1.234 # negative float
|
||||
1.2e3 # float
|
||||
7E-10 # float
|
||||
|
||||
# Strings are surrounded by double quotes. They almost work the same as PHP double quoted strings.
|
||||
# A string can be written in multiple lines. The line break character is then ignored by the reader.
|
||||
|
||||
"hello world"
|
||||
|
||||
"this is\na\nstring"
|
||||
|
||||
"this
|
||||
is
|
||||
a
|
||||
string."
|
||||
|
||||
"use backslack to escape \" string"
|
||||
|
||||
"the dollar must not be escaped: $ or $abc just works"
|
||||
|
||||
|
||||
# Collections & Sequences
|
||||
#############
|
||||
|
||||
# Lists are linked-list data structures, while vectors are array-backed.
|
||||
(type '(1 2 3)) # :list
|
||||
(type [1 2 3]) # :vector
|
||||
|
||||
# A list would be written as just (1 2 3), but we have to quote
|
||||
# it to stop the reader thinking it's a function.
|
||||
# Also, (list 1 2 3) is the same as '(1 2 3)
|
||||
|
||||
# You can produce a (non-lazy) sequence between a range.
|
||||
(range 1 10 2) # <- (range from to step)
|
||||
(take 4 (range 10))
|
||||
|
||||
# Use cons to add an item to the beginning of a list
|
||||
(cons 4 '(1 2 3)) # => (4 1 2 3)
|
||||
|
||||
# Use push to add, and put to replace an item in a vector
|
||||
(push [1 2 3] 4) # => (1 2 3 4)
|
||||
(put [1 2 3] 1 4) # => (1 4 3)
|
||||
|
||||
# Use concat to add lists or vectors together
|
||||
(concat [1 2] '(3 4)) # => [1 2 3 4]
|
||||
|
||||
# Use filter, map to interact with collections
|
||||
(map inc [1 2 3]) # => [2 3 4]
|
||||
(filter even? [1 2 3]) # => [2]
|
||||
|
||||
# Use reduce to reduce them. The initial-value is mandatory
|
||||
(reduce + 0 [1 2 3 4])
|
||||
# => (+ (+ (+ 1 2) 3) 4)
|
||||
# => 10
|
||||
|
||||
(reduce push [] '(3 2 1))
|
||||
# = (push (push (push [] 3) 2) 1)
|
||||
# => [3 2 1]
|
||||
|
||||
# Functions
|
||||
#############
|
||||
|
||||
# Use fn to create new functions. A function always returns its last statement.
|
||||
(fn [] "Hello World") # => :function
|
||||
|
||||
# You need extra parens to call it
|
||||
((fn [] "Hello World")) # => "Hello World"
|
||||
|
||||
# You can create a var using def
|
||||
(def x 1)
|
||||
x # => 1
|
||||
|
||||
# Assign a function to a var
|
||||
(def hello-world (fn [] "Hello World"))
|
||||
(hello-world) # => "Hello World"
|
||||
|
||||
# You can shorten this process by using defn
|
||||
(defn hello-world [] "Hello World")
|
||||
|
||||
# The [] is the list of arguments for the function.
|
||||
(defn hello [name]
|
||||
(str "Hello " name))
|
||||
(hello "Jens") # => "Hello Jens"
|
||||
|
||||
# You can also use this shorthand to create functions:
|
||||
(def hello2 |(str "Hello " $1))
|
||||
(hello2 "Anna") # => "Hello Anna"
|
||||
|
||||
# Functions can pack extra arguments up in a seq for you
|
||||
(defn count-args [& args]
|
||||
(str "You passed " (count args) " args: " args))
|
||||
(count-args 1 2 3) # => "You passed 3 args: @[1 2 3]"
|
||||
|
||||
# You can mix regular and packed arguments
|
||||
(defn hello-count [name & args]
|
||||
(str "Hello " name ", you passed " (count args) " extra args"))
|
||||
(hello-count "Jesus" 1 2) # => "Hello Jesus, you passed 2 extra args"
|
||||
|
||||
|
||||
# Maps
|
||||
#############
|
||||
|
||||
# Hash maps have faster lookups but don't retain key order.
|
||||
(type {:a 1 :b 2 :c 3}) # => :hash-map
|
||||
(type (hash-map :a 1 :b 2 :c 3)) # => :hash-map
|
||||
|
||||
# Maps can use any hashable type as a key, but usually keywords are best
|
||||
# Keywords are like strings with some efficiency bonuses and they start with `:`
|
||||
(type :a) # => :keyword
|
||||
|
||||
(def stringmap {"a" 1 "b" 2 "c" 3})
|
||||
stringmap # => {"a" 1 "b" 2 "c" 3}
|
||||
|
||||
(def keymap {:a 1 :b 2 :c 3})
|
||||
keymap # => {:a 1 :c 3 :b 2}
|
||||
|
||||
# Retrieve a value from a map by calling it as a function
|
||||
(stringmap "a") # => 1
|
||||
(keymap :a) # => 1
|
||||
|
||||
# Keywords can be used to retrieve their value from a map, too!
|
||||
(:b keymap) # => 2
|
||||
|
||||
# Don't try this with strings.
|
||||
# ("a" stringmap)
|
||||
# ...Exception: Call to undefined function a()
|
||||
|
||||
# Retrieving a non-present key returns nil
|
||||
(stringmap "d") # => nil
|
||||
|
||||
# Use put to add new keys to hash-maps
|
||||
(def newkeymap (put keymap :d 4))
|
||||
newkeymap # => {:a 1 :b 2 :c 3 :d 4}
|
||||
|
||||
# But remember, phel types are immutable!
|
||||
keymap # => {:a 1 :b 2 :c 3}
|
||||
|
||||
# Use unset to remove keys
|
||||
(unset keymap :a) # => {:b 2 :c 3}
|
||||
|
||||
# Sets
|
||||
#############
|
||||
|
||||
# A Set contains unique values in random order.
|
||||
|
||||
(type (set 1 2 3)) # => :set
|
||||
(set 1 2 3 1 2 3 3 2 1 3 2 1) # => (set 1 2 3)
|
||||
|
||||
# Add a member with push
|
||||
(push (set 1 2 3) 4) # => (set 1 2 3 4)
|
||||
|
||||
# Remove one with unset
|
||||
(unset (set 1 2 3) 1) # => (set 2 3)
|
||||
|
||||
# Test for existence by using the set as a function:
|
||||
((set 1 2 3) 1) # => 1
|
||||
((set 1 2 3) 4) # => nil
|
||||
|
||||
# There are more functions like: count, union, intersection, difference, etc.
|
||||
|
||||
|
||||
# Useful forms
|
||||
#############
|
||||
|
||||
# Logic constructs in clojure are just macros, and look like everything else
|
||||
(if false "a" "b") # => "b"
|
||||
(if false "a") # => nil
|
||||
|
||||
# Use let to create temporary bindings
|
||||
(let [a 1 b 2]
|
||||
(> a b)) # => false
|
||||
|
||||
# Group statements together with do
|
||||
(do
|
||||
(print "Hello")
|
||||
"World") #=> "World" (prints "Hello")
|
||||
|
||||
# Functions have an implicit do
|
||||
(defn print-and-say-hello [name]
|
||||
(print "Saying hello to " name)
|
||||
(str "Hello " name))
|
||||
(print-and-say-hello "Jeff") #=> "Hello Jeff" (prints "Saying hello to Jeff")
|
||||
|
||||
# So does let
|
||||
(let [name "Urkel"]
|
||||
(print "Saying hello to " name)
|
||||
(str "Hello " name)) # => "Hello Urkel" (prints "Saying hello to Urkel")
|
||||
|
||||
# Use the threading macros (-> and ->>) to express transformations of
|
||||
# data more clearly.
|
||||
|
||||
# The "Thread-first" macro (->) inserts into each form the result of
|
||||
# the previous, as the first argument (second item)
|
||||
(->
|
||||
{:a 1 :b 2}
|
||||
(put :c 3) #=> (put {:a 1 :b 2} :c 3)
|
||||
(unset :b)) #=> (unset (put {:a 1 :b 2} :c 3) :b)
|
||||
|
||||
|
||||
# The double arrow does the same thing, but inserts the result of
|
||||
# each line at the *end* of the form. This is useful for collection
|
||||
# operations in particular:
|
||||
(->>
|
||||
(range 10)
|
||||
(map inc) #=> (map inc (range 10))
|
||||
(filter odd?)) #=> (filter odd? (map inc (range 10)))
|
||||
# Result: [1 3 5 7 9]
|
||||
|
||||
|
||||
# When you are in a situation where you want more freedom as where to
|
||||
# put the result of previous data transformations in an
|
||||
# expression, you can use the as-> macro. With it, you can assign a
|
||||
# specific name to transformations' output and use it as a
|
||||
# placeholder in your chained expressions:
|
||||
|
||||
(as-> [1 2 3] input
|
||||
(map inc input) #=> You can use last transform's output at the last position
|
||||
(get input 2) #=> and at the second position, in the same expression
|
||||
(push [4 5 6] input 8 9 10)) #=> or in the middle !
|
||||
# Result: [4 5 6 4 8 9 10]
|
||||
|
||||
# PHP
|
||||
#################
|
||||
|
||||
# PHP has a huge and useful standard library, and you're able to use
|
||||
# all native functions with the prefix `php/`.
|
||||
(php/+ 1 2 3)
|
||||
|
||||
# With :use you can use different namespaces. Similar as `use` in PHP.
|
||||
(ns my\module
|
||||
(:use \DateTimeImmutable))
|
||||
|
||||
# You can import functions from other phel files with :require
|
||||
(ns my\module
|
||||
(:require phel\test :refer [deftest is]))
|
||||
|
||||
# Use the class name with a "php/new" to make a new instance
|
||||
(php/new \DateTime) # <a date-time object>
|
||||
|
||||
# Use php/-> to call methods of an object
|
||||
(def d (php/new \DateTime))
|
||||
(php/-> d (getTimestamp)) # <a timestamp>
|
||||
|
||||
# you can do it in one line too
|
||||
(php/-> (php/new \DateTime) (getTimestamp))
|
||||
|
||||
# Use php/:: to call static methods
|
||||
(php/:: \DateTimeImmutable ATOM) # <a timestamp>
|
||||
```
|
||||
|
||||
### Further Reading
|
||||
|
||||
This is far from exhaustive, but hopefully it's enough to get you on your feet.
|
||||
|
||||
Read the full documentation in the website: [https://phel-lang.org/](https://phel-lang.org/documentation/getting-started/)
|
Loading…
Reference in New Issue
Block a user