DEV Community

Cover image for Introduction to Functional Programming with Typescript
Tyler Roberts
Tyler Roberts

Posted on • Edited on

Introduction to Functional Programming with Typescript

This is the beginning of a series of short guides on functional programming, much of what I've posted on reddit, I'm compiling for dev.to.


I finally went back to try and learn functional programming, and realized that the type structure of the function composition is in itself data! My old friend Zac tried to teach me this many years ago, et voila. This helped me better understand currying and higher order functions. For example:

const K  = a => b => a;   
const KI = a => b => b; 
Enter fullscreen mode Exit fullscreen mode

K is technically a Kestrel combinator or first function or constant function. KI is a Kite combinator or second function. You can copy and paste these into the console (F12) and test it out.

K(1)(0)  // ->  1
KI(1)(0) // ->  0
Enter fullscreen mode Exit fullscreen mode

I think Typescript actually helps us here to make sense of what we're going to do next so let's create a type association.

const T = K 
const F = KI

// pretend we're in a .ts file  
type BOOL = T | F; 
Enter fullscreen mode Exit fullscreen mode

So BOOL simply says it's either T or F (true or false) like any basic Boolean.

Keep in mind that T and F are still just functions K and KI

So we can create an AND function that takes T and T and returns True; or F and T and returns False. This AND function will look a bit weird but bare with me.

const AND = p => q => p(q)(p);   
Enter fullscreen mode Exit fullscreen mode

We pass in a function to parameter p and curry another argument (function) to parameter q so that p(q)(p) will return us a function of p. These are higher order functions or curried functions that we define and expect to pass around in our program. This is just a discipline to writing programs and comes from lambda calculus. Immediately this will probably seem very odd, and I'll try to break it down.

AND(T) gives us q => p(q)(p), or essentially q => T(q)(T). Well we know T returns whatever it's first argument is(a => b => a). So if q is F it will be F.

We can satisfy parameter q with argument F. AND(T)(F) or T(F)(T). We're not calling F but passing it in. Now this means we satisfy every functional parameter for T.

Essentially AND(T)(F) becomes T(F)(T) but what's different is AND sets up the second parameter as first argument in T. You can run T(T)(F) in the console and get a => b => a. T(F)(T) is a => b => b .. imagine AND(T)(F) as T => F => T(F)(T).

If this doesn't immediately make sense that's ok (I'm trying my best 😅). We can simply run it in the browser instead!

AND(T)(T)  // -> a => b => a 
AND(T)(F)  // -> a => b => b 
AND(F)(T)  // -> a => b => b 
AND(F)(F)  // -> a => b => b   
Enter fullscreen mode Exit fullscreen mode

The resulting value from AND(T)(T) is the function composition we passed in, T! So the type association of a => b => a as we've already defined in our program (or console in this case) is typeof T in type BOOL.

// main.ts
// Compiles! ✅
const K = a => b => a;
const KI = a => b => b;

type TRUE = typeof K;
type FALSE = typeof KI;

type BOOL = TRUE | FALSE;

const AND: BOOL = p => q => p(q)(p);

(async (): Promise<FALSE> => {
    const falsey: BOOL = AND(K)(KI);
    return falsey;
})();

Enter fullscreen mode Exit fullscreen mode

Now you're more likely to see pure FO in other languages like lisp, clojure or haskell so if this scares you, don't worry! There's a reason C# is OO and F# is FO, trying to write FO in something like typescript is cumbersome without default lazy loaded lists etc.
I think running it in the browser first hand (in js) is a great way to learn how the composition of functions itself can be treated as data.

I find this fascinating. There's definitely a better explanation out there but this is how I came to understand it after finally revisiting it. That euphoric feeling of when something clicks after many months of attempts to understand something is the best and never gets old!


Next: How to compose a NOT function -- ⏭⌛

Top comments (0)