Last active
August 14, 2016 23:04
-
-
Save tfausak/d6441efdd7e4938fb8807379ae7f3021 to your computer and use it in GitHub Desktop.
Alternative function application. http://try.purescript.org/?gist=d6441efdd7e4938fb8807379ae7f3021
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- This module explores an alternative function application syntax for | |
-- PureScript. It grew out of my Neon library, which uses some similar ideas. | |
-- <https://github.com/tfausak/purescript-neon> | |
module Main where | |
import Control.Monad.Eff as Eff | |
import Control.Monad.Eff.Console as Console | |
import Prelude (bind) | |
import Prelude as Prelude | |
-- Let's start by defining a function that does backwards function application. | |
-- We won't be using this directly, but we need it to define an operator. | |
_operator_apply :: forall a b. a -> (a -> b) -> b | |
_operator_apply x f = f x | |
-- And now let's define that operator. PureScript's prelude calls this `#`. In | |
-- Haskell it's `&`. In Elm it's `|>`. We're going with `..` to make things | |
-- look similar to OOP languages. (PureScript doesn't let us define an operator | |
-- called `.`, which is probably a good thing.) | |
infixl 8 _operator_apply as .. | |
-- That operator encourages us to write functions with signatures like this: | |
-- method :: ... -> a -> b | |
-- Where `a` is the "object" we're working with, `...` are the arguments to | |
-- this "method", and `b` is the return value. | |
-- What should the arguments to one of these "methods" look like? We could use | |
-- normal function arguments like this: | |
-- method :: c -> d -> a -> b | |
-- Where `c` and `d` are arguments. But it may not always be clear what `c` and | |
-- `d` are supposed to be. PureScript usually solves this problem with | |
-- newtypes, documentation, or both. But some languages, like Python and Swift, | |
-- use named arguments. We can emulate that in PureScript like so: | |
-- method :: { foo :: c, bar :: d } -> a -> b | |
-- Now it's clear what the arguments are supposed to be. | |
-- Let's convert one of the prelude's functions to this style. The `clamp` | |
-- function is a good candidate. Its signature looks like this: | |
-- clamp :: Ord a => a -> a -> a -> a | |
clamp :: forall a. Prelude.Ord a => { min :: a, max :: a } -> a -> a | |
clamp { min: l, max: h } x = Prelude.clamp l h x | |
-- So what does actually using a "method" like this look like? Let's see. | |
main :: Eff.Eff (console :: Console.CONSOLE) Prelude.Unit | |
main = do | |
Console.log "https://gist.github.com/tfausak/d6441efdd7e4938fb8807379ae7f3021" | |
-- We can of course call it the "normal" way. | |
Console.logShow (clamp { min: 1, max: 9 } 5) -- 5 | |
-- But I think it's a little better to use our `..` operator. | |
Console.logShow (5..clamp{ min: 1, max: 9 }) -- 5 | |
-- We can even go all in and use `..` for everything. This forms a single | |
-- expression that reads left to right. | |
5..clamp{ min: 1, max: 9 }..Console.logShow -- 5 | |
-- There are a few downsides to this way of doing things: | |
-- 1. The PureScript community doesn't do things this way, so it's weird. | |
-- 2. You don't get curried (partially applied) functions for free. I don't | |
-- mind this so much because I rarely use curried functions, but it may be a | |
-- bigger deal for some people. | |
-- 3. Optional arguments still have to be wrapped in `Maybe`. You can't just | |
-- leave them out like you can in other languages. | |
-- 4. It's not clear if the record holding all of a function's arguments should | |
-- be "open" (like `{ | r }`) or not. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Lots of good discussion on Twitter: https://twitter.com/taylorfausak/status/764843723213508609