summaryrefslogtreecommitdiffhomepage
path: root/phel.html.markdown
blob: 39d8bae29f5141b8e6b31eccec2c4f5fdf9d3ca7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
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/)