The Phel LanguageThe official website of the Phel language. Phel is a functional programming language that compiles to PHPZola2023-11-01T00:00:00+00:00https://phel-lang.org/atom.xmlRelease: v0.12.02023-11-01T00:00:00+00:002023-11-01T00:00:00+00:00https://phel-lang.org/blog/release-0-12/<h2 id="better-error-handling">Better error handling</h2>
<ul>
<li>Fix <code>phel repl</code> when not in the phel directory</li>
<li>Fix <code>AtomParser</code> decimal regex</li>
<li>Do not create the entrypoint when namespace is not defined</li>
<li>Improve <code>ExceptionHandler</code></li>
<li>Move phel to <code>bin/phel</code></li>
<li>Define custom <code>--version</code></li>
<li>Notify user when running non-existing file</li>
</ul>
<h2 id="full-list">Full list</h2>
<p>For a full list of changes, have a look at the <a href="https://github.com/phel-lang/phel-lang/blob/master/CHANGELOG.md">Changelog</a>.</p>
Release: v0.11.02023-08-26T00:00:00+00:002023-08-26T00:00:00+00:00https://phel-lang.org/blog/release-0-11/<h2 id="improved-build">Improved build</h2>
<ul>
<li>Improve PHP notices and error messages</li>
<li>Create a main script to run after build</li>
</ul>
<h2 id="full-list">Full list</h2>
<p>For a full list of changes, have a look at the <a href="https://github.com/phel-lang/phel-lang/blob/master/CHANGELOG.md">Changelog</a>.</p>
Release: v0.10.02023-04-01T00:00:00+00:002023-04-01T00:00:00+00:00https://phel-lang.org/blog/release-0-10/<h2 id="support-for-fluid-configuration">Support for fluid configuration</h2>
<p>Added support for fluid configuration in <code>phel-config.php</code></p>
<pre data-lang="php" style="background-color:#ffffff;color:#4d4d4c;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#f5871f;"><?php
</span><span style="color:#999999;"># Instead of a raw array...
</span><span style="color:#8959a8;">return </span><span>[
</span><span> </span><span style="color:#718c00;">'src-dirs' </span><span style="color:#3e999f;">=> </span><span>[</span><span style="color:#718c00;">'some/directory'</span><span>],
</span><span> </span><span style="color:#718c00;">'test-dirs' </span><span style="color:#3e999f;">=> </span><span>[</span><span style="color:#718c00;">'another/directory'</span><span>],
</span><span> </span><span style="color:#718c00;">'vendor-dir' </span><span style="color:#3e999f;">=> </span><span style="color:#718c00;">'vendor'</span><span>,
</span><span> </span><span style="color:#718c00;">'out-dir' </span><span style="color:#3e999f;">=> </span><span style="color:#718c00;">'out'</span><span>,
</span><span> </span><span style="color:#718c00;">'export' </span><span style="color:#3e999f;">=> </span><span>[
</span><span> </span><span style="color:#718c00;">'target-directory' </span><span style="color:#3e999f;">=> </span><span style="color:#718c00;">'src/Generated'</span><span>,
</span><span> </span><span style="color:#718c00;">'directories' </span><span style="color:#3e999f;">=> </span><span>[</span><span style="color:#718c00;">'some/other/dir'</span><span>],
</span><span> </span><span style="color:#718c00;">'namespace-prefix' </span><span style="color:#3e999f;">=> </span><span style="color:#718c00;">'Generated'</span><span>,
</span><span> ],
</span><span> </span><span style="color:#718c00;">'ignore-when-building' </span><span style="color:#3e999f;">=> </span><span>[</span><span style="color:#718c00;">'src/ignore.me'</span><span>],
</span><span> </span><span style="color:#718c00;">'keep-generated-temp-files' </span><span style="color:#3e999f;">=> </span><span style="color:#f5871f;">true</span><span>,
</span><span> </span><span style="color:#718c00;">'format-dirs' </span><span style="color:#3e999f;">=> </span><span>[</span><span style="color:#718c00;">'src'</span><span>, </span><span style="color:#718c00;">'tests'</span><span>],
</span><span>];
</span><span>
</span><span style="color:#999999;"># ...you can now use a fluent object interface
</span><span style="color:#8959a8;">return </span><span>(</span><span style="color:#8959a8;">new </span><span style="color:#c99e00;">PhelConfig</span><span>())
</span><span> </span><span style="color:#4271ae;">-></span><span style="color:#c82829;">setSrcDirs</span><span style="color:#4271ae;">([</span><span style="color:#718c00;">'some/directory'</span><span style="color:#4271ae;">])
</span><span> </span><span style="color:#4271ae;">-></span><span style="color:#c82829;">setTestDirs</span><span style="color:#4271ae;">([</span><span style="color:#718c00;">'another/directory'</span><span style="color:#4271ae;">])
</span><span> </span><span style="color:#4271ae;">-></span><span style="color:#c82829;">setVendorDir</span><span style="color:#4271ae;">(</span><span style="color:#718c00;">'vendor'</span><span style="color:#4271ae;">)
</span><span> </span><span style="color:#4271ae;">-></span><span style="color:#c82829;">setOutDir</span><span style="color:#4271ae;">(</span><span style="color:#718c00;">'out'</span><span style="color:#4271ae;">)
</span><span> </span><span style="color:#4271ae;">-></span><span style="color:#c82829;">setExport</span><span style="color:#4271ae;">((</span><span style="color:#8959a8;">new </span><span style="color:#c99e00;">PhelExportConfig</span><span style="color:#4271ae;">())
</span><span style="color:#4271ae;"> -></span><span style="color:#c82829;">setDirectories</span><span style="color:#4271ae;">([</span><span style="color:#718c00;">'some/other/dir'</span><span style="color:#4271ae;">])
</span><span style="color:#4271ae;"> -></span><span style="color:#c82829;">setNamespacePrefix</span><span style="color:#4271ae;">(</span><span style="color:#718c00;">'Generated'</span><span style="color:#4271ae;">)
</span><span style="color:#4271ae;"> -></span><span style="color:#c82829;">setTargetDirectory</span><span style="color:#4271ae;">(</span><span style="color:#718c00;">'src/Generated'</span><span style="color:#4271ae;">))
</span><span> </span><span style="color:#4271ae;">-></span><span style="color:#c82829;">setIgnoreWhenBuilding</span><span style="color:#4271ae;">([</span><span style="color:#718c00;">'src/ignore.me'</span><span style="color:#4271ae;">])
</span><span> </span><span style="color:#4271ae;">-></span><span style="color:#c82829;">setKeepGeneratedTempFiles</span><span style="color:#4271ae;">(</span><span style="color:#f5871f;">true</span><span style="color:#4271ae;">)
</span><span> </span><span style="color:#4271ae;">-></span><span style="color:#c82829;">setFormatDirs</span><span style="color:#4271ae;">([</span><span style="color:#718c00;">'src'</span><span style="color:#4271ae;">, </span><span style="color:#718c00;">'tests'</span><span style="color:#4271ae;">])</span><span>;
</span></code></pre>
<p>Both examples above produce the same outcome.</p>
<h2 id="default-format-paths">Default format paths</h2>
<p>You can now define default directories to apply when using the <code>phel format</code> command without arguments.</p>
<pre data-lang="bash" style="background-color:#ffffff;color:#4d4d4c;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#c82829;">vendor/bin/phel</span><span style="color:#4271ae;"> format</span><span style="color:#f5871f;"> -h
</span><span style="color:#c82829;">Description:
</span><span> </span><span style="color:#c82829;">Formats</span><span style="color:#4271ae;"> the given files.
</span><span>
</span><span style="color:#c82829;">Usage:
</span><span> </span><span style="color:#c82829;">format </span><span style="color:#8959a8;">[</span><span style="color:#4271ae;"><paths>...</span><span style="color:#8959a8;">]
</span><span>
</span><span style="color:#c82829;">Arguments:
</span><span> </span><span style="color:#c82829;">paths</span><span style="color:#4271ae;"> The file paths that you want to format. </span><span style="color:#8959a8;">[</span><span style="color:#4271ae;">default: [</span><span style="color:#718c00;">"src"</span><span style="color:#4271ae;">,</span><span style="color:#718c00;">"tests"</span><span style="color:#8959a8;">]</span><span style="color:#4271ae;">]
</span></code></pre>
<p>You can define these dirs in your <code>phel-config.php</code>: <code>PhelConfig::setFormatDirs(['src',...])</code></p>
<h2 id="added-testdox-to-the-test-command">Added --testdox to the test command</h2>
<p>Added <code>--testdox</code> option to <code>phel test</code> command. Inspired by PHPUnit, in phel you will see the test name plus the assertion description instead of a dot.</p>
<p>Without <code>--testdox</code>:</p>
<pre data-lang="bash" style="background-color:#ffffff;color:#4d4d4c;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#c82829;">php</span><span style="color:#4271ae;"> phel test</span><span style="color:#f5871f;"> --filter</span><span style="color:#4271ae;"> test-json-encode-flag
</span><span style="color:#c82829;">..
</span></code></pre>
<p>With <code>--testdox</code>:</p>
<pre data-lang="bash" style="background-color:#ffffff;color:#4d4d4c;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#c82829;">phel-lang/</span><span style="color:#4271ae;"> (master</span><span>) </span><span style="color:#c82829;">$</span><span style="color:#4271ae;"> php phel test</span><span style="color:#f5871f;"> --filter</span><span style="color:#4271ae;"> test-json-encode-flag</span><span style="color:#f5871f;"> --testdox
</span><span style="color:#c82829;">✔</span><span style="color:#4271ae;"> test-json-encode-flag: It tests flags parameter with one flag.
</span><span style="color:#c82829;">✔</span><span style="color:#4271ae;"> test-json-encode-flags: It tests flags parameter with two flags.
</span><span>
</span></code></pre>
<h2 id="other-improvements">Other improvements</h2>
<ul>
<li>Enable gacela cache filesystem by default</li>
<li>Deprecate <em>compile-mode</em> in favor of <em>build-mode</em></li>
<li>Fix php/apush, php/aset and php/aunset for global php arrays</li>
</ul>
<h2 id="full-list">Full list</h2>
<p>For a full list of changes have a look at the <a href="https://github.com/phel-lang/phel-lang/blob/master/CHANGELOG.md">Changelog</a>.</p>
Release: v0.9.02023-02-05T00:00:00+00:002023-02-05T00:00:00+00:00https://phel-lang.org/blog/release-0-9/<p>This release adds new config parameters and improve the compiling performance.</p>
<h2 id="apifacade">ApiFacade</h2>
<p>You can use the ApiFacade from your PHP project to get all phel functions. For example, this is used to render the API page: <a href="https://phel-lang.org/api">https://phel-lang.org/api</a>.</p>
<h2 id="new-doc-command">New <code>doc</code> command</h2>
<pre data-lang="bash" style="background-color:#ffffff;color:#4d4d4c;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#c82829;">$</span><span style="color:#4271ae;"> php phel doc</span><span style="color:#f5871f;"> --help
</span><span style="color:#c82829;">Description:
</span><span> </span><span style="color:#c82829;">Display</span><span style="color:#4271ae;"> the docs for any/all phel functions.
</span><span>
</span><span style="color:#c82829;">Usage:
</span><span> </span><span style="color:#c82829;">doc </span><span style="color:#8959a8;">[</span><span style="color:#4271ae;">options</span><span style="color:#8959a8;">] [</span><span style="color:#4271ae;">--</span><span style="color:#8959a8;">] [</span><span style="color:#4271ae;"><search></span><span style="color:#8959a8;">]
</span><span>
</span><span style="color:#c82829;">Arguments:
</span><span> </span><span style="color:#c82829;">search</span><span style="color:#4271ae;"> Search input that look for a similar function name </span><span style="color:#8959a8;">[</span><span style="color:#4271ae;">default: </span><span style="color:#718c00;">""</span><span style="color:#8959a8;">]
</span><span>
</span><span style="color:#c82829;">Options:
</span><span> </span><span style="color:#c82829;">--ns</span><span style="color:#4271ae;"> Specify which namespaces to load. </span><span style="color:#8959a8;">[</span><span style="color:#4271ae;">optional</span><span style="color:#8959a8;">]
</span></code></pre>
<h2 id="new-config-parameters">New config parameters</h2>
<h3 id="ignore-when-building">ignore-when-building</h3>
<p>This key accept a list of file-names/patterns which will be ignore at building time.</p>
<p>You might have a <code>src/local.phel</code> file for your local testing/ideas, and so you want to keep it out of the building process.</p>
<h3 id="keep-generated-temp-files">keep-generated-temp-files</h3>
<p>This key, <code>'keep-generated-temp-files'</code> will decide if you want to remove or keep the generated files <code>(default: false)</code>.</p>
<p>Every time we run any phel command (like run, test, doc, etc...), this generates temporal files (with the executable PHP code), which later get <code>required</code> and run inside the PHP interpreter.</p>
<p>However, these files are useless after the execution, and running another command will generate a bunch of new files again. The temporal files will get removed at some point, but these files are not needed at all by default; although, this could be useful for debugging.</p>
<h2 id="other-improvements">Other improvements</h2>
<ul>
<li>Rename command <code>phel compile</code> to <code>phel build</code></li>
<li>Allow underscores in decimal numbers</li>
</ul>
<h2 id="full-list">Full list</h2>
<p>For a full list of changes have a look at the <a href="https://github.com/phel-lang/phel-lang/blob/master/CHANGELOG.md">Changelog</a>.</p>
Release: v0.8.02023-01-16T00:00:00+00:002023-01-16T00:00:00+00:00https://phel-lang.org/blog/release-0-8/<p>This release adds new JSON functions to Phel.</p>
<h2 id="json">JSON</h2>
<p>You can use the <code>phel\json</code> namespace to encode or decode a JSON directly from Phel.</p>
<h3 id="encode">Encode</h3>
<p>You can encode a Phel data structure to JSON using the <code>encode</code> function.</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span>(</span><span style="color:#8959a8;">ns</span><span> my-namespace
</span><span> (</span><span style="color:#3e999f;">:require</span><span> phel\json))
</span><span>
</span><span>(json/encode [</span><span style="color:#f5871f;">1 2 3</span><span>]) </span><span style="color:#999999;"># "[1,2,3]"
</span><span>(json/encode {</span><span style="color:#3e999f;">:key1 </span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">:key2 </span><span style="color:#718c00;">"value2"</span><span>} </span><span style="color:#999999;"># "{\"key1\":1,\"key2\":\"value2\"}"
</span></code></pre>
<h3 id="decode">Decode</h3>
<p>You can decode a JSON string to a Phel data structure using the <code>decode</code> function.</p>
<p>Considering <code>your.json</code> as:</p>
<pre data-lang="json" style="background-color:#ffffff;color:#4d4d4c;" class="language-json "><code class="language-json" data-lang="json"><span>{
</span><span> </span><span style="color:#718c00;">"name"</span><span>: </span><span style="color:#718c00;">"John Doe"</span><span>,
</span><span> </span><span style="color:#718c00;">"age"</span><span>: </span><span style="color:#f5871f;">30</span><span>,
</span><span> </span><span style="color:#718c00;">"image"</span><span>: </span><span style="color:#718c00;">""</span><span>,
</span><span> </span><span style="color:#718c00;">"email"</span><span>: </span><span style="color:#718c00;">"john@example.com"</span><span>,
</span><span> </span><span style="color:#718c00;">"phone"</span><span>: </span><span style="color:#718c00;">"(912) 555-4321"</span><span>,
</span><span> </span><span style="color:#718c00;">"url"</span><span>: </span><span style="color:#718c00;">"https://example.com"</span><span>,
</span><span> </span><span style="color:#718c00;">"location"</span><span>: {
</span><span> </span><span style="color:#718c00;">"address"</span><span>: </span><span style="color:#718c00;">"2712 Broadway St"</span><span>,
</span><span> </span><span style="color:#718c00;">"postalCode"</span><span>: </span><span style="color:#718c00;">"CA 94115"</span><span>,
</span><span> </span><span style="color:#718c00;">"countryCode"</span><span>: </span><span style="color:#718c00;">"US"
</span><span> }
</span><span>}
</span></code></pre>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span>(</span><span style="color:#8959a8;">ns</span><span> my-namespace
</span><span> (</span><span style="color:#3e999f;">:require</span><span> phel\json))
</span><span>
</span><span>(</span><span style="color:#8959a8;">def</span><span> your-json (</span><span style="color:#8959a8;">php/file_get_contents </span><span>(str __DIR__ </span><span style="color:#718c00;">"/your.json"</span><span>)))
</span><span>
</span><span>(json/decode your-json)
</span><span style="color:#999999;"># Evaluates to
</span><span style="color:#999999;"># {
</span><span style="color:#999999;"># :name "John Doe"
</span><span style="color:#999999;"># :age 30
</span><span style="color:#999999;"># :image ""
</span><span style="color:#999999;"># :email "john@example.com"
</span><span style="color:#999999;"># :phone "(912) 555-4321"
</span><span style="color:#999999;"># :url "https://example.com"
</span><span style="color:#999999;"># :location {
</span><span style="color:#999999;"># :address "2712 Broadway St"
</span><span style="color:#999999;"># :postalCode "CA 94115"
</span><span style="color:#999999;"># :countryCode "US"
</span><span style="color:#999999;"># }
</span><span style="color:#999999;"># }
</span></code></pre>
<h2 id="other-improvements">Other improvements</h2>
<ul>
<li>Allow strings on <code>empty?</code></li>
<li>Improved error message when using strings on <code>count</code></li>
<li>Added <code>contains-value?</code> function</li>
</ul>
<h2 id="full-list">Full list</h2>
<p>For a full list of changes have a look at the <a href="https://github.com/phel-lang/phel-lang/blob/master/CHANGELOG.md">Changelog</a>.</p>
Release: v0.7.02022-05-05T00:00:00+00:002022-05-05T00:00:00+00:00https://phel-lang.org/blog/release-0-7/<p>This release comes with a lot a small improvements to the core library.</p>
<h2 id="merge-datastructures">Merge datastructures</h2>
<p>Merging maps or nested data structures is a common problem in programming. The new functions <code>merge-with</code> and <code>deep-merge</code> solve these problems in Phel.</p>
<p>The first function <code>merge-with</code> takes a two-arity function <code>f</code> and a unlimited number of hash maps. It will merge these hash maps as in <code>merge</code> but in case two maps have the same key it will call <code>f</code> with the left and the right value. Example:</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span>(merge-with + {</span><span style="color:#3e999f;">:a </span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">:b </span><span style="color:#f5871f;">2</span><span>} {</span><span style="color:#3e999f;">:b </span><span style="color:#f5871f;">3 </span><span style="color:#3e999f;">:c </span><span style="color:#f5871f;">4</span><span>}) </span><span style="color:#999999;"># Evaluates to {:a 1 :b 5 :c 4}
</span></code></pre>
<p>The second function <code>deep-merge</code> will recursively merge nested data structures. Hash maps will be merged as in <code>merge</code>, vectors will be concatenated and sets will be united. Example:</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span>(deep-merge {</span><span style="color:#3e999f;">:a </span><span>{</span><span style="color:#3e999f;">:b </span><span>{</span><span style="color:#3e999f;">:c </span><span>[</span><span style="color:#3e999f;">:d</span><span>]}}} {</span><span style="color:#3e999f;">:a </span><span>{</span><span style="color:#3e999f;">:b </span><span>{</span><span style="color:#3e999f;">:c </span><span>[</span><span style="color:#3e999f;">:e</span><span>] </span><span style="color:#3e999f;">:f :g</span><span>}}} </span><span style="color:#999999;"># Evaluates to {:a {:b {:c [:d :e] :f :g}}}
</span></code></pre>
<h2 id="improved-phel-http-namespace">Improved phel\http namespace</h2>
<p>The namespace <code>phel\http</code> go a little facelift:</p>
<ul>
<li>The function <code>http/uri-from-string</code> was added. This lets you create a URI struct from a string</li>
<li>The function <code>http/response-from-map</code> was added. This function create a response struct from a hash map.</li>
<li>The function <code>http/response-from-string</code> was added. This function creates a response from a string function.</li>
<li>The request struct got a new additional field: <code>attributes</code>. This field can be used store custom data to enrich a request.</li>
<li>The function <code>http/request-from-map</code> was added. This function create a request struct from a hash map.</li>
</ul>
<h2 id="read-eval-and-compile">Read, eval and compile</h2>
<p>The core library has now some additional function to call parts of the compiler directly in your code.</p>
<p>First, the <code>read-string</code> function will parse a string and return a Phel expression.</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span>(read-string </span><span style="color:#718c00;">"(+ 1 1)"</span><span>) </span><span style="color:#999999;"># Evaluates to '(+ 1 1)
</span></code></pre>
<p>Second, the <code>eval</code> function let's you evaluate a Phel expression:</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span>(eval '(+ </span><span style="color:#f5871f;">1 1</span><span>)) </span><span style="color:#999999;"># Evaluates to 2
</span></code></pre>
<p>Finally, the <code>compile</code> function returns the compiled PHP code for a give Phel expression:</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span>(compile '(+ </span><span style="color:#f5871f;">1 1</span><span>)) </span><span style="color:#999999;"># Evaluates to "(1 + 1);"
</span></code></pre>
<h2 id="other-changes">Other changes</h2>
<p>A few other changes and bug fixes have been done in this version. For a full list of changes have a look at the <a href="https://github.com/phel-lang/phel-lang/blob/master/CHANGELOG.md">Changelog</a>.</p>
Release: v0.6.02022-02-02T00:00:00+00:002022-02-02T00:00:00+00:00https://phel-lang.org/blog/release-0-6/<p>We just released version 0.6.0. This release drops the support for PHP 7.4. Phel supports now PHP 8.0 and PHP 8.1. Read the rest of the thread for other important changes</p>
<p>The for loop supports now a <code>:reduce</code> option. This allows you to use the list comprehension loop for task that normally would required to use the reduce function.</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span style="color:#999999;"># Instead of
</span><span>(reduce + </span><span style="color:#f5871f;">0 </span><span>[</span><span style="color:#f5871f;">1 2 3</span><span>])
</span><span style="color:#999999;"># Do
</span><span>(</span><span style="color:#8959a8;">for </span><span>[x </span><span style="color:#3e999f;">:in </span><span>[</span><span style="color:#f5871f;">1 2 3</span><span>]
</span><span> </span><span style="color:#3e999f;">:reduce </span><span>[acc </span><span style="color:#f5871f;">0</span><span>]]
</span><span> (+ acc x))
</span></code></pre>
<p>This becomes very useful if you combine it with other options of the for loop. Instead of using map, filter and reduce individually you can now do everything in a for loop.</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span style="color:#999999;"># Instead of
</span><span>(->> [</span><span style="color:#f5871f;">1 2 3 4</span><span>]
</span><span> (filter even?)
</span><span> (map inc)
</span><span> (reduce + </span><span style="color:#f5871f;">0</span><span>))
</span><span>
</span><span style="color:#999999;"># Do
</span><span>(</span><span style="color:#8959a8;">for </span><span>[x </span><span style="color:#3e999f;">:in </span><span>[</span><span style="color:#f5871f;">1 2 3 4</span><span>]
</span><span> </span><span style="color:#3e999f;">:when </span><span>(even? x)
</span><span> </span><span style="color:#3e999f;">:reduce </span><span>[acc </span><span style="color:#f5871f;">0</span><span>]]
</span><span> (+ acc (inc x)))
</span></code></pre>
<p>Next, it is now possible to require a PHP file in the namespace statement. This is very handy to load the autoload.php file from composer.</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span>(</span><span style="color:#8959a8;">ns</span><span> myapp
</span><span> (</span><span style="color:#3e999f;">:require-file </span><span style="color:#718c00;">"vendor/autoload.php"</span><span>))
</span></code></pre>
<p>Finally, a new function was added to the core library. The <code>coerce-in</code> function will bound a value in a given range.</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span>(coerce-in </span><span style="color:#f5871f;">0.5 0 1</span><span>) </span><span style="color:#999999;"># evaluates to 0.5
</span><span>(coerce-in </span><span style="color:#f5871f;">1.5 0 1</span><span>) </span><span style="color:#999999;"># evaluates to 1
</span><span>(coerce-in </span><span style="color:#f5871f;">-0.5 0 1</span><span>) </span><span style="color:#999999;"># evaluates to 0
</span></code></pre>
<p>All other changes can be found the in the <a href="https://github.com/phel-lang/phel-lang/blob/master/Changelog.md">Changelog</a></p>
Map, filter and reduce2022-01-25T00:00:00+00:002022-01-25T00:00:00+00:00https://phel-lang.org/blog/map-filter-reduce/<p>Phel, as many other functional programming languages, comes with three basic tools you should learn right from the beginning:</p>
<p>Map, filter and reduce.</p>
<ul>
<li>Map transforms one sequence into another sequence of the same length.</li>
<li>Filter removes elements from a sequence depending on some predicate function.</li>
<li>Reduce takes a sequence of elements and aggregates it into some value.</li>
</ul>
<p>Let's see them in action:</p>
<p>The map function takes two arguments. The first argument is a one-argument function that transforms a single value. The second argument is the sequence that should be transformed.</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span style="color:#999999;"># Increment by 1
</span><span>(map inc [</span><span style="color:#f5871f;">1 2 3</span><span>]) </span><span style="color:#999999;"># evaluates to [2 3 4]
</span><span style="color:#999999;"># Multiply by 2 using fn syntax
</span><span>(map (</span><span style="color:#8959a8;">fn </span><span>[x] (* </span><span style="color:#f5871f;">2</span><span> x)) [</span><span style="color:#f5871f;">1 2 3</span><span>]) </span><span style="color:#999999;"># evaluates to [2 4 6]
</span><span style="color:#999999;"># Multiply by 2 using fn shorthand syntax
</span><span>(map |(* </span><span style="color:#f5871f;">2</span><span> $) [</span><span style="color:#f5871f;">1 2 3</span><span>]) </span><span style="color:#999999;"># evaluates to [2 4 6]
</span></code></pre>
<p>The filter function takes two arguments. The first argument is a one-argument function that returns true if it should keep the value in the list. The second argument is the sequence that should be filtered.</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span style="color:#999999;"># keep even numbers
</span><span>(filter even? [</span><span style="color:#f5871f;">1 2 3</span><span>]) </span><span style="color:#999999;"># evaluates to [2]
</span><span style="color:#999999;"># keep odd numbers
</span><span>(filter odd? [</span><span style="color:#f5871f;">1 2 3</span><span>]) </span><span style="color:#999999;"># evaluates to [1 3]
</span><span style="color:#999999;"># keep number bigger 2
</span><span>(filter |(> $ </span><span style="color:#f5871f;">2</span><span>) [</span><span style="color:#f5871f;">1 2 3</span><span>]) </span><span style="color:#999999;"># evaluates to [3]
</span></code></pre>
<p>The reduce function takes three arguments. The first argument is a two-argument function (accumulated value and sequence value) that return a new accumulated value. The second argument is the initial accumulated value and the third argument is the sequence that should be reduced.</p>
<pre data-lang="phel" style="background-color:#ffffff;color:#4d4d4c;" class="language-phel "><code class="language-phel" data-lang="phel"><span style="color:#999999;"># sum all value starting by 0
</span><span>(reduce + </span><span style="color:#f5871f;">0 </span><span>[</span><span style="color:#f5871f;">1 2 3</span><span>]) </span><span style="color:#999999;"># evaluates to 6
</span><span style="color:#999999;"># sum all values use first value as starting point
</span><span>(reduce (</span><span style="color:#8959a8;">fn </span><span>[acc x] (* acc x)) </span><span style="color:#f5871f;">1 </span><span>[</span><span style="color:#f5871f;">2 3 4</span><span>]) </span><span style="color:#999999;"># evaluates to 24
</span><span style="color:#999999;"># concat all numbers to a string
</span><span>(reduce str </span><span style="color:#718c00;">"" </span><span>[</span><span style="color:#f5871f;">1 2 3</span><span>]) </span><span style="color:#999999;"># evaluates to "123"
</span></code></pre>
Release: v0.5.02021-12-17T00:00:00+00:002021-12-17T00:00:00+00:00https://phel-lang.org/blog/release-0-5/<p>This release adds a compile command and three new language features:</p>
<h2 id="variables">Variables</h2>
<p>Variables provide a way to manage mutable state. Each variable contains a single value. To create a new variable use the var function: <code>(var value)</code></p>
<h2 id="namespaced-keywords">Namespaced Keywords</h2>
<p>If code or data is shared to the outside world keywords can collide. This problem is now solved with namespaced keywords.</p>
<h2 id="interfaces">Interfaces</h2>
<p>For better abstraction it is now possible to define interfaces in Phel. Structs can now implement these interfaces.</p>
Release: v0.4.02021-10-05T00:00:00+00:002021-10-05T00:00:00+00:00https://phel-lang.org/blog/release-0-4/<p>In the forth release of Phel a lot of changes happened to the machine room of Phel. The way Phel compiles code has changed from a bottom up approach to a top-down approach.</p>
Release: v0.3.02021-05-12T00:00:00+00:002021-05-12T00:00:00+00:00https://phel-lang.org/blog/release-0-3/<p>The third release of Phel make big improvement in the data structure. Phel now uses persistent data structures. The old data structures are marked as deprecated and will be removed in future versions.</p>
<p>Other changes:</p>
<ul>
<li>Rename fmt command to format</li>
<li>Added new function take-last</li>
<li>Added new function re-seq</li>
<li>Added new function contains? (#267)</li>
<li>Bug fixes</li>
</ul>
Release: v0.2.02021-02-22T00:00:00+00:002021-02-22T00:00:00+00:00https://phel-lang.org/blog/release-0-2/<p>With this release it is possible to call Phel functions from PHP. Additionally, it is possible to set PHP object properties from Phel.</p>
Release: v0.1.02021-01-31T00:00:00+00:002021-01-31T00:00:00+00:00https://phel-lang.org/blog/release-0-1/<p>After a year of work the very first version of Phel is released. Yeah!</p>
My attempt on functional programming in PHP2020-06-05T00:00:00+00:002020-06-05T00:00:00+00:00https://phel-lang.org/blog/functional-programming-in-php/<p>PHP was one of my first languages I learned. Even so, this dates back over 10 years, I still use PHP every day at work. However, in the meantime I also learned a lot of other languages like Java, Clojure, Scala, Python, Javascript and Scheme. By learning all the languages and their concepts, the concept of functional programming always was my favorite, and so I tried to make my PHP programming style more functional. In the following article you can read some approaches I tried.</p>
<h2 id="functions-as-arguments">Functions as arguments</h2>
<p>Defining a function in PHP is quite easy</p>
<pre data-lang="php" style="background-color:#ffffff;color:#4d4d4c;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#f5871f;"><?php
</span><span>
</span><span style="color:#8959a8;">function </span><span style="color:#4271ae;">inc</span><span>(</span><span style="color:#f5871f;">$number</span><span>) {
</span><span> </span><span style="color:#8959a8;">return </span><span style="color:#c82829;">$number </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">1</span><span>;
</span><span>}
</span></code></pre>
<p>However, one of the most common things you do in functional programming is to pass a function as argument to another function. If you try that, PHP will respond to you with an error message.</p>
<pre data-lang="php" style="background-color:#ffffff;color:#4d4d4c;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#f5871f;"><?php
</span><span>
</span><span style="color:#c82829;">$x </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>];
</span><span style="color:#4271ae;">array_map(</span><span style="color:#666969;">inc</span><span style="color:#4271ae;">, </span><span style="color:#c82829;">$x</span><span style="color:#4271ae;">)</span><span>;
</span><span style="color:#999999;">// PHP Warning: Use of undefined constant inc
</span></code></pre>
<p>PHP has no direct support for this. To fix the problem we must convert the symbol <code>inc</code> into a string.</p>
<pre data-lang="php" style="background-color:#ffffff;color:#4d4d4c;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#f5871f;"><?php
</span><span>
</span><span style="color:#c82829;">$x </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>];
</span><span style="color:#4271ae;">array_map(</span><span style="color:#718c00;">'inc'</span><span style="color:#4271ae;">, </span><span style="color:#c82829;">$x</span><span style="color:#4271ae;">)</span><span>;
</span></code></pre>
<p>A more common way is to define a constant for each function and use the constant as parameter to the function.</p>
<pre data-lang="php" style="background-color:#ffffff;color:#4d4d4c;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#f5871f;"><?php
</span><span>
</span><span style="color:#8959a8;">function </span><span style="color:#4271ae;">inc</span><span>(</span><span style="color:#f5871f;">$number</span><span>) {
</span><span> </span><span style="color:#8959a8;">return </span><span style="color:#c82829;">$number </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">1</span><span>;
</span><span>}
</span><span style="color:#8959a8;">const </span><span style="color:#666969;">inc </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">'inc'</span><span>;
</span><span>
</span><span style="color:#c82829;">$x </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>];
</span><span style="color:#4271ae;">array_map(</span><span style="color:#666969;">inc</span><span style="color:#4271ae;">, </span><span style="color:#c82829;">$x</span><span style="color:#4271ae;">)</span><span>;
</span></code></pre>
<p>The disadvantage of such an approach is, that refactoring is more challenging and the chance of missing a constant definition is very high.</p>
<h2 id="class-modules">Class modules</h2>
<p>Another common concept in functional programming is the possibility to group a set of function to a module. A module can have a few public accessible functions and some private functions that are not accessible to the outside world. In PHP this can be accomplished by creating a class using only static members.</p>
<pre data-lang="php" style="background-color:#ffffff;color:#4d4d4c;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#f5871f;"><?php
</span><span>
</span><span style="color:#8959a8;">class </span><span style="color:#c99e00;">MyStaticModule </span><span>{
</span><span> </span><span style="color:#8959a8;">private static function </span><span style="color:#4271ae;">add</span><span>(</span><span style="color:#f5871f;">$a</span><span>, </span><span style="color:#f5871f;">$b</span><span>) {
</span><span> </span><span style="color:#8959a8;">return </span><span style="color:#c82829;">$a </span><span style="color:#3e999f;">+ </span><span style="color:#c82829;">$b</span><span>;
</span><span> }
</span><span>
</span><span> </span><span style="color:#8959a8;">public static function </span><span style="color:#4271ae;">inc</span><span>(</span><span style="color:#f5871f;">$number</span><span>) {
</span><span> </span><span style="color:#8959a8;">return </span><span style="color:#c82829;">self</span><span style="color:#4271ae;">::</span><span style="color:#c82829;">add</span><span style="color:#4271ae;">(</span><span style="color:#c82829;">$number</span><span style="color:#4271ae;">, </span><span style="color:#f5871f;">1</span><span style="color:#4271ae;">)</span><span>;
</span><span> }
</span><span>}
</span></code></pre>
<p>This approach gives us all the flexibility in terms of modularity. However, the problem of passing functions as arguments for another function hasn't been solved.</p>
<h2 id="trait-modules">Trait modules</h2>
<p>A second approach on modularity is to use traits.</p>
<pre data-lang="php" style="background-color:#ffffff;color:#4d4d4c;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#f5871f;"><?php
</span><span>
</span><span style="color:#8959a8;">trait </span><span>MyTraitModule {
</span><span>
</span><span> </span><span style="color:#8959a8;">public function </span><span style="color:#4271ae;">inc</span><span>(</span><span style="color:#f5871f;">$number</span><span>) {
</span><span> </span><span style="color:#8959a8;">return </span><span style="color:#c82829;">$number </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">1</span><span>;
</span><span> }
</span><span>}
</span></code></pre>
<p>After defining all traits you have to create a class that uses these traits.</p>
<pre data-lang="php" style="background-color:#ffffff;color:#4d4d4c;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#f5871f;"><?php
</span><span>
</span><span style="color:#8959a8;">class </span><span style="color:#c99e00;">MyProgram </span><span>{
</span><span> </span><span style="color:#8959a8;">use </span><span style="color:#718c00;">MyTraitModule</span><span>;
</span><span> </span><span style="color:#999999;">// use Other Traits here
</span><span>
</span><span> </span><span style="color:#8959a8;">public function </span><span style="color:#4271ae;">main</span><span>() {
</span><span> </span><span style="color:#8959a8;">return </span><span style="color:#c82829;">$this</span><span style="color:#4271ae;">-></span><span style="color:#c82829;">inc</span><span style="color:#4271ae;">(</span><span style="color:#c82829;">$number</span><span style="color:#4271ae;">)</span><span>;
</span><span> }
</span><span>}
</span></code></pre>
<p>A benefit of this approach is that we can solve our first problem. Therefore, we just need to define the magic method <code>__get</code>.</p>
<pre data-lang="php" style="background-color:#ffffff;color:#4d4d4c;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#f5871f;"><?php
</span><span>
</span><span style="color:#8959a8;">trait </span><span>FPMagic {
</span><span> </span><span style="color:#8959a8;">public function </span><span style="color:#4271ae;">__get</span><span>(</span><span style="color:#f5871f;">$name</span><span>) {
</span><span> </span><span style="color:#8959a8;">if </span><span>(</span><span style="color:#4271ae;">method_exists(</span><span style="color:#c82829;">$this</span><span style="color:#4271ae;">, </span><span style="color:#c82829;">$name</span><span style="color:#4271ae;">)</span><span>) {
</span><span> </span><span style="color:#8959a8;">return </span><span>[</span><span style="color:#c82829;">$this</span><span>, </span><span style="color:#c82829;">$name</span><span>]; </span><span style="color:#999999;">// This is callable
</span><span> } </span><span style="color:#8959a8;">else </span><span>{
</span><span> </span><span style="color:#8959a8;">throw new </span><span>\</span><span style="color:#c99e00;">Exception</span><span>(</span><span style="color:#718c00;">'Method does not exists: ' </span><span style="color:#3e999f;">. </span><span style="color:#c82829;">$name</span><span>);
</span><span> }
</span><span> }
</span><span>}
</span><span>
</span><span style="color:#8959a8;">class </span><span style="color:#c99e00;">MyProgram </span><span>{
</span><span> </span><span style="color:#8959a8;">use </span><span style="color:#718c00;">FPMagic</span><span>;
</span><span> </span><span style="color:#8959a8;">use </span><span style="color:#718c00;">MyTraitModule</span><span>;
</span><span>
</span><span> </span><span style="color:#8959a8;">public function </span><span style="color:#4271ae;">main</span><span>() {
</span><span> </span><span style="color:#c82829;">$x </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>];
</span><span> </span><span style="color:#8959a8;">return </span><span style="color:#4271ae;">array_map(</span><span style="color:#c82829;">$this</span><span style="color:#4271ae;">-></span><span style="color:#c82829;">inc</span><span style="color:#4271ae;">, </span><span style="color:#c82829;">$x</span><span style="color:#4271ae;">)</span><span>; </span><span style="color:#999999;">// Works using the magic method __get
</span><span> }
</span><span>}
</span></code></pre>
<p>A big disadvantage of this approach is, that we have to resolve all conflicting function names ourselves. Not only for public methods but also for private methods. This becomes next to impossible if the program grows.</p>
<p>A combination of the class module approach and the trait module approach would be a good solution to get started with functional programming in PHP. However, the trick with the magic method <code>_get</code> cannot be used for the class module approach, since PHP has no magic method for static properties.</p>
<h2 id="alternatives">Alternatives</h2>
<p>One last alternative is to use a language that is functional and compiles to PHP. In recent years, there have been a few attempts on this (e.g. <a href="https://haxe.org/">Haxe</a> and <a href="http://www.pharen.org/">Pharen</a>). While Pharen looked very promising, it hasn't seen any commits for a few years now and is still based on PHP 5.</p>
<h2 id="introducing-phel">Introducing Phel</h2>
<p>Since I didn't find a good solution for this problem, I used my coronavirus spare time to solve it by myself. This is what turns out to be Phel. Phel is a functional programming language that compiles to PHP. It is a dialect of Lisp inspired by <a href="https://clojure.org/">Clojure</a> and <a href="https://janet-lang.org/">Janet</a>. While the status of Phel is currently alpha, it is fairly complete. If you want to try it, go ahead and read the <a href="/documentation/getting-started/">Getting started guide</a>. I'm looking for your feedback.</p>