A blog exploring functional programming and Swift.

2020 Year-in-review

Wednesday Dec 23, 2020

It’s the end of the year again, and we’re feeling nostalgic 😊. We’re really proud of everything we produced for 2020, so join us for a quick review of some of our favorite highlights.

We are also offering 25% off the first year for first-time subscribers. If you’ve been on the fence on whether or not to subscribe, now is the time!

Highlights

At a high-level, this year we saw:

But these high-level stats don’t scratch the surface of what we covered in 2020:

The Composable Architecture

In May we finally concluded the core series of episodes (17 hours of video!) that introduce a holistic approach to application architecture, known as The Composable Architecture. We highlighted 5 core problems any architecture must solve, and showed how to solve them: state management, composability, modularity, side effects, and testing.

To celebrate the end of the core series of episodes we open sourced a library for making it easy to adopt the ideas of the Composable Architecture in your application. In only 8 months the library has over 2,700 stars, merged more than 200 pull requests, and believe it or not, there’s still more to come in 2021 😀.

Dependencies

We tackled dependencies head on in a 5-part series where we precisely describe what dependencies are and why the make our code complex, and then show what to do about it. We build a moderately complex application from scratch, one that uses API requests, network connectivity APIs, and location manager APIs, and show how to wrangle those dependencies into something simple, flexible, and testable.

If you’ve ever reached for a protocol to control a dependency, then this is the series for you.

Parsing

We picked up parsing again this year after having first covered it more than a year ago. This time we focused our attention on two main things. First, we generalized the parser library so that it can parse any kind of input into any kind of output. This allows to use the same code to parse many different things, including strings, binary data, URL requests and more. Then we turned our attention to performance. We showed that parser combinators can be extremely performant, nearly as performant as highly-tuned, hand-written, ad-hoc parsers.

And all of this culminated into the release of 0.1.0 of Parsing, a parsing library with a focus on composability, generality and performance.

Combine Schedulers

Apple’s Combine framework is incredibly powerful, and there are lots of great resources out there for learning the core concepts behind the framework. However, a topic that doesn’t get a lot of attention is schedulers. We devoted an entire series of episodes to understanding schedulers in depth, and then open sourced a library for making better use of schedulers in Combine.

Case Paths

Continuing a long tradition of asking “if structs have it, then why don’t enums too?” we explore what it would mean if enums had something like key paths defined for them. We show that such a concept can be naturally defined for enums, but we called them case paths. They turn out to be the perfect tool for transforming the actions of reducers, and even SwiftUI bindings that hold onto enum-based state.

New project: isowords

We ended the year by announcing a brand new project: isowords. It’s a game built in Swift (even the backend is Swift!), and it makes use of nearly every concept discussed on Point-Free, such as the Composable Architecture, dependencies, parsers, random number generators, algebraic data types, and more. We will be releasing the game early next year, and we’ll have a lot more to say about how it was built soon (we also have a few beta spots open, contact us if you’re interested!).

🎉 2021 🎉

We’re thankful to all of our subscribers for supporting us and helping us create this content. To celebrate the end of the year we are also offering 25% off the first year for first-time subscribers. If you’ve been on the fence on whether or not to subscribe, now is the time!

See you in 2021!


Open Sourcing Parsing

Monday Dec 21, 2020

We are excited to announce the 0.1.0 release of Parsing, a library for turning nebulous data into well-structured data. It was built from the content of 21 episodes (10 hours) where we show how to build a parsing library from scratch, with a focus on composition, performance, and generality:

  • Composition: The ability to break large, complex parsing problems down into smaller, simpler ones. And the ability to take small, simple parsers and easily combine them into larger, more complex ones.

  • Performance: Parsers that have been composed of many smaller parts should perform as well as highly-tuned, hand-written parsers.

  • Generality: The ability to parse any kind of input into any kind of output. This allows you to choose which abstraction levels you want to work with based on how much performance you need or how much correctness you want guaranteed. For example, you can write a highly tuned parser on collections of UTF-8 code units, and it will automatically plug into parsers of strings, arrays, unsafe buffer pointers and more.

Motivation

Parsing is a surprisingly ubiquitous problem in programming. We can define parsing as trying to take a more nebulous blob of data and transform it into something more well-structured. The Swift standard library comes with a number of parsers that we reach for every day. For example, there are initializers on Int, Double, and even Bool, that attempt to parse numbers and booleans from strings:

Int("42")         // 42
Int("Hello")      // nil

Double("123.45")  // 123.45
Double("Goodbye") // nil

Bool("true")      // true
Bool("0")         // nil

And there are types like JSONDecoder and PropertyListDecoder that attempt to parse Decodable-conforming types from data:

try JSONDecoder().decode(User.self, from: data)
try PropertyListDecoder().decode(Settings.self, from: data)

While parsers are everywhere in Swift, Swift has no holistic story for parsing. Instead, we typically parse data in an ad hoc fashion using a number of unrelated initializers, methods, and other means. And this typically leads to less maintainable, less reusable code.

This library aims to write such a story for parsing in Swift. It introduces a single unit of parsing that can be combined in interesting ways to form large, complex parsers that can tackle the programming problems you need to solve in a maintainable way.

Getting started

Suppose you have a string that holds some user data that you want to parse into an array of Users:

var input = """
1,Blob,true
2,Blob Jr.,false
3,Blob Sr.,true
"""

struct User {
  var id: Int
  var name: String
  var isAdmin: Bool
}

A naive approach to this would be a nested use of .split(separator:), and then a little bit of extra work to convert strings into integers and booleans:

let users = input
  .split(separator: "\n")
  .compactMap { row -> User? in
    let fields = row.split(separator: ",")
    guard
      fields.count == 3,
      let id = Int(fields[0]),
      let isAdmin = Bool(String(fields[2]))
    else { return nil }

    return User(id: id, name: String(fields[1]), isAdmin: isAdmin)
  }

Not only is this code a little messy, but it is also inefficient since we are allocating arrays for the .split and then just immediately throwing away those values.

It would be more straightforward and efficient to instead describe how to consume bits from the beginning of the input and convert that into users. This is what this parser library excels at 😄.

We can start by describing what it means to parse a single row, first by parsing an integer off the front of the string, and then parsing a comma that we discard using the .skip operator:

let user = Int.parser()
  .skip(StartsWith(","))

Already this can consume the beginning of the input:

user.parse(&input) // => 1
input // => "Blob,true\n2,Blob Jr.,false\n3,Blob Sr.,true"

Next we want to take everything up until the next comma for the user’s name, and then skip the comma:

let user = Int.parser()
  .skip(StartsWith(","))
  .take(PrefixWhile { $0 != "," })
  .skip(StartsWith(","))

Here the .take operator has combined parsed values together into a tuple, (Int, Substring).

And then we want to take the boolean at the end of the row for the user’s admin status:

let user = Int.parser()
  .skip(StartsWith(","))
  .take(PrefixWhile { $0 != "," })
  .skip(StartsWith(","))
  .take(Bool.parser())

Currently this will parse a tuple (Int, Substring, Bool) from the input, and we can .map on that to turn it into a User:

let user = Int.parser()
  .skip(StartsWith(","))
  .take(PrefixWhile { $0 != "," })
  .skip(StartsWith(","))
  .take(Bool.parser())
  .map { User(id: $0, name: String($1), isAdmin: $2) }

That is enough to parse a single user from the input string:

user.parse(&input) // => User(id: 1, name: "Blob", isAdmin: true)
input // => "\n2,Blob Jr.,false\n3,Blob Sr.,true"

To parse multiple users from the input we can use the Many parser:

let users = Many(user, separator: StartsWith("\n"))

user.parse(&input) // => [User(id: 1, name: "Blob", isAdmin: true), ...]
input // => ""

Now this parser can process an entire document of users, and the code is simpler and more straightforward than the version that uses .split and .compactMap.

Even better, it’s more performant. We’ve written benchmarks for these two styles of parsing, and the .split-style of parsing is more than twice as slow:

name                             time        std        iterations
------------------------------------------------------------------
README Example.Parser: Substring 3426.000 ns ±  63.40 %     385395
README Example.Adhoc             7631.000 ns ±  47.01 %     169332
Program ended with exit code: 0

Further, if you are willing write your parsers against UTF8View instead of Substring, you can eke out even more performance, more than doubling the speed:

name                             time        std        iterations
------------------------------------------------------------------
README Example.Parser: Substring 3693.000 ns ±  81.76 %     349763
README Example.Parser: UTF8      1272.000 ns ± 128.16 %     999150
README Example.Adhoc             8504.000 ns ±  59.59 %     151417

We can also compare these times to a tool that Apple’s Foundation gives us: Scanner. It’s a type that allows you to consume from the beginning of strings in order to produce values, and provides a nicer API than using .split:

var users: [User] = []
while scanner.currentIndex != input.endIndex {
  guard
    let id = scanner.scanInt(),
    let _ = scanner.scanString(","),
    let name = scanner.scanUpToString(","),
    let _ = scanner.scanString(","),
    let isAdmin = scanner.scanBool()
  else { break }

  users.append(User(id: id, name: name, isAdmin: isAdmin))
  _ = scanner.scanString("\n")
}

However, the Scanner style of parsing is more than 5 times as slow as the substring parser written above, and more than 15 times slower than the UTF-8 parser:

name                             time         std        iterations
-------------------------------------------------------------------
README Example.Parser: Substring  3481.000 ns ±  65.04 %     376525
README Example.Parser: UTF8       1207.000 ns ± 110.96 %    1000000
README Example.Adhoc              8029.000 ns ±  44.44 %     163719
README Example.Scanner           19786.000 ns ±  35.26 %      62125

That’s the basics of parsing a simple string format, but there’s a lot more operators and tricks to learn in order to performantly parse larger inputs. View the benchmarks for examples of real life parsing scenarios.

Design

Protocol

The design of the library is largely inspired by the Swift standard library and Apple’s Combine framework. A parser is represented as a protocol that many types conform to, and then parser transformations (also known as “combinators”) are methods that return concrete types conforming to the parser protocol.

For example, to parse all the characters from the beginning of a substring until you encounter a comma you can use the Prefix parser:

let parser = Prefix<Substring> { $0 != "," }

var input = "Hello,World"[...]
parser.parse(&input) // => "Hello"
input // => ",Hello"

The type of this parser is:

Prefix<Substring>

We can .map on this parser in order to transform its output, which in this case is the string “Hello”:

let parser = Prefix<Substring> { $0 != "," }
  .map { $0 + "!!!" }

var input = "Hello,World"[...]
parser.parse(&input) // => "Hello!!!"
input // => ",Hello"

The type of this parser is now:

Parsers.Map<Prefix<Substring>, Substring>

Notice how the type of the parser encodes the operations that we performed. This adds a bit of complexity when using these types, but comes with some performance benefits because Swift can usually optimize the creation of those nested types.

Low-level versus high-level

The library makes it easy to choose which abstraction level you want to work on. Both low-level and high-level have their pros and cons.

Parsing low-level inputs, such as UTF-8 code units, has better performance, but at the cost of potentially losing correctness. A canonical example of this is trying to parse the character “é”, which can be represented in code units as [233] or [101, 769]. If you don’t remember to always parse both representations you may have a bug where you accidentally fail your parser when it encounters a code unit sequence you don’t support.

On the other hand, parsing high-level inputs, such as String, can guarantee correctness, but at the cost of performance. For example, String handles the complexities of extended grapheme clusters and UTF-8 normalization for you, but traversing strings is slower since its elements are variable width.

The library gives you the tools that allow you to choose which abstraction level you want to work on, as well as the ability to fluidly move between abstraction levels where it makes sense.

For example, say we want to parse particular city names from the beginning of a string:

enum City {
  case london
  case newYork
  case sanJose
}

Because “San José” has an accented character, the safest way to parse it is to parse on the Substring abstraction level:

let city = StartsWith<Substring>("London").map { City.london }
  .orElse(StartsWith("New York").map { .newYork })
  .orElse(StartsWith("San José").map { .sanJose })

var input = "San José,123"
city.parse(&input) // => City.sanJose
input // => ",123"

However, we are incurring the cost of parsing Substring for this entire parser, even though only the “San José” case needs that power. We can refactor this parser so that “London” and “New York” are parsed on the UTF8View level, since they consist of only ASCII characters, and then parse “San José” as Substring:

let city = StartsWith("London".utf8).map { City.london }
  .orElse(StartsWith("New York".utf8).map { .newYork })
  .orElse(StartsWith("San José").utf8.map { .sanJose })

It’s subtle, but StartsWith("London".utf8) is a parser that parses the code units for “London” from the beginning of a UTF8View, whereas StartsWith("San José").utf8 parses “San José” as a Substring, and then converts that into a UTF8View parser.

This allows you to parse as much as possible on the more performant, low-level UTF8View, while still allowing you to parse on the more correct, high-level Substring when necessary.

Benchmarks

This library comes with a benchmark executable that not only demonstrates the performance of the library, but also provides a wide variety of parsing examples:

These are the times we currently get when running the benchmarks:

MacBook Pro (16-inch, 2019)
2.4 GHz 8-Core Intel Core i9
64 GB 2667 MHz DDR4

name                                         time             std          iterations
-------------------------------------------------------------------------------------
Arithmetic.Parser                                12622.000 ns ±  40.63 %       102408
BinaryData.Parser                                  512.000 ns ± 172.80 %      1000000
Bool.Bool.init                                      28.000 ns ± 880.63 %      1000000
Bool.BoolParser                                     43.000 ns ± 423.22 %      1000000
Bool.Scanner.scanBool                              920.000 ns ± 119.49 %      1000000
Color.Parser                                       127.000 ns ± 341.57 %      1000000
CSV.Parser                                     1370906.000 ns ±  12.24 %         1027
CSV.Ad hoc mutating methods                    1338824.500 ns ±  13.91 %         1014
Date.Parser                                      12429.000 ns ±  38.26 %       107342
Date.DateFormatter                               41168.000 ns ±  29.40 %        31353
Date.ISO8601DateFormatter                        56236.000 ns ±  27.39 %        23383
HTTP.HTTP                                         3850.000 ns ± 1898.35 %      341642
JSON.Parser                                       6115.000 ns ±  45.95 %       217152
JSON.JSONSerialization                            3050.000 ns ±  71.43 %       431524
Numerics.Int.init                                   38.000 ns ± 655.10 %      1000000
Numerics.Int.parser                                 41.000 ns ± 464.80 %      1000000
Numerics.Scanner.scanInt                           145.000 ns ± 22359.78 %    1000000
Numerics.Comma separated: Int.parser           5511505.000 ns ±   8.87 %          245
Numerics.Comma separated: Scanner.scanInt     82824843.000 ns ±   2.37 %           17
Numerics.Comma separated: String.split       117376272.000 ns ±   2.68 %           11
Numerics.Double.init                                58.000 ns ± 518.12 %      1000000
Numerics.Double.parser                              59.000 ns ± 445.11 %      1000000
Numerics.Scanner.scanDouble                        195.000 ns ± 234.94 %      1000000
Numerics.Comma separated: Double.parser        6222693.000 ns ±   9.33 %          220
Numerics.Comma separated: Scanner.scanDouble  89431780.500 ns ±   3.75 %           16
Numerics.Comma separated: String.split        33387660.000 ns ±   4.02 %           41
PrefixUpTo.Parser                                22898.000 ns ±  34.40 %        58197
PrefixUpTo.Scanner.scanUpToString               162816.000 ns ±  18.55 %         8000
Race.Parser                                      29962.000 ns ±  32.24 %        43186
README Example.Parser: Substring                  3451.000 ns ±  59.72 %       378685
README Example.Parser: UTF8                       1247.000 ns ± 110.74 %      1000000
README Example.Adhoc                              8134.000 ns ±  34.87 %       161121
Routing.Parser                                    5242.000 ns ±  52.70 %       249596
String Abstractions.Substring                  1044908.500 ns ±  12.95 %         1296
String Abstractions.UTF8                        138412.000 ns ±  22.64 %         8938
Xcode Logs.Parser                              6980962.000 ns ±   7.61 %          197

Try it today

Head over to the Parsing repository to try the library out today. For some inspiration of things you might like to write parsers for check out the benchmarks in the project.


The Composable Architecture and SwiftUI Alerts

Tuesday Jun 30, 2020

Today we are releasing a new version of the Composable Architecture with helpers that make working with SwiftUI alerts and action sheets a breeze.

Because the Composable Architecture demands that all data flow through the application in a single direction, we cannot leverage SwiftUI’s two-way bindings directly because they can make changes to state without going through a reducer. This means we can’t use the standard API to display alerts and sheets without manually deriving these bindings.

However, the library now comes with two new types, AlertState and ActionSheetState, which can be used in your application to control the presentation, dismissal, and logic of alerts and action sheets.

For example, suppose you have a delete button that when tapped it will show an alert asking the user to confirm their deletion. You can model the actions of tapping the delete button, confirming the deletion, as well as canceling the deletion, in your domain’s action enum:

enum AppAction: Equatable {
  case alertCancelTapped
  case alertConfirmTapped
  case deleteButtonTapped

  // Your other actions
}

And you can model the state for showing the alert in your domain’s state, which can start at nil to represent “dismissed”:

struct AppState: Equatable {
  var alert: AlertState<AppAction>?

  // Your other state
}

Then, in your reducer you can construct an AlertState value to represent the alert you want to show the user:

let appReducer = Reducer<AppState, AppAction, AppEnvironment> { state, action, environment in
  switch action
    case .deleteButtonTapped:
      state.alert = AlertState(
        title: "Delete",
        message: "Are you sure you want to delete this? It cannot be undone.",
        primaryButton: .default("Confirm", send: .alertConfirmTapped),
        secondaryButton: .cancel()
      )
      return .none

    case .alertCancelTapped:
      state.alert = nil
      return .none

    case .alertConfirmTapped:
      state.alert = nil
      // Do deletion logic...
  }
}

And then, in your view you can use the .alert(_:dismiss:) method on View in order to present the alert in a way that works best with the Composable Architecture:

Button("Delete") { viewStore.send(.deleteTapped) }
  .alert(
    self.store.scope(state: \.alert),
    dismiss: .alertCancelTapped
  )

This makes your reducer in complete control of when the alert is shown or dismissed, and makes it so that any choice made in the alert is automatically fed back into the reducer so that you can handle its logic.

Even better, you can instantly write tests that your alert behavior works as expected:

let store = TestStore(
  initialState: AppState(),
  reducer: appReducer,
  environment: .mock
)

store.assert(
  .send(.deleteTapped) {
    $0.alert = AlertState(
      title: "Delete",
      message: "Are you sure you want to delete this? It cannot be undone.",
      primaryButton: .default("Confirm", send: .alertConfirmTapped),
      secondaryButton: .cancel()
    )
  },
  .send(.deleteTapped) {
    $0.alert = nil
    // Also verify that delete logic executed correctly
  }
)

Clean up your alert and action sheet logic today

We’ve just released version version 0.6.0 of the Composable Architecture, so you can start using these new helpers immediately. Let us know what you think!


Older blog posts

Monday Jun 22, 2020

Core Motion support in the Composable Architecture

We are releasing our second mini-library for the Composable Architecture, which makes it easy to use Core Motion.

Monday Jun 15, 2020

Open Sourcing CombineSchedulers

Today we are open-sourcing CombineSchedulers, a library that introduces a few schedulers that makes working with Combine more testable and more versatile.

Wednesday May 27, 2020

Instrumenting features built in the Composable Architecture

Today we are releasing first-party support for instrumenting features built in the Composable Architecture.

Wednesday May 20, 2020

Core Location support in the Composable Architecture

We are releasing a mini-library that makes it easy to use Core Location inside the Composable Architecture.

Tuesday May 12, 2020

Regional Discounts

Regional discounts takes 50% off a monthly or yearly personal subscription to anyone whose credit card is issued from a particular list of countries.

Monday May 4, 2020

Composable Architecture, the library

Today we are releasing the Composable Architecture as an open-source library. It is a way to build applications in a consistent and understandable way, with composition, testing and ergonomics in mind.

Wednesday Mar 11, 2020

Announcing Episode Collections

After over two years and nearly 100 episodes we are finally launching episode collections on Point-Free!

Friday Feb 21, 2020

Share Point-Free with friends and save!

Today we're excited to announce support for referral bonuses! When friends and colleagues of yours subscribe to Point-Free with your referral link, both of you will get one month free.

Tuesday Feb 4, 2020

Open Sourcing Case Paths

Today we are open sourcing CasePaths, a library that introduces the power and ergonomics of key paths to enums!

Monday Dec 30, 2019

2019 Year-in-review

Random number generators, parsers, SwiftUI, composable architecture and more! Join us for a review of everything we accomplished in 2019!

Monday Dec 23, 2019

Snapshot Testing SwiftUI

Snapshot testing gives us broad test coverage on our SwiftUI views with very little up front work.

Wednesday Dec 18, 2019

Free Video: Testing SwiftUI

A free video exploring how to test SwiftUI.

Wednesday Nov 20, 2019

A Crash Course in Combine

Two free videos exploring Apple's new Combine framework, its core components, and how to integrate it in your code.

Thursday Nov 7, 2019

Higher-Order Snapshot Testing

How to enrich snapshot testing strategies with additional behavior using higher-order constructions.

Tuesday Jul 30, 2019

SwiftUI and State Management Corrections

Xcode 11 beta 5 has brought lots of changes to SwiftUI, and we'd like to take a moment to provide corrections to our episodes based on these changes.

Thursday May 9, 2019

Enterprise Subscriptions

Point-Free now supports enterprise subscriptions, making it easier than ever to manage a team subscription for larger organizations! For a fixed yearly rate, everyone with an email from your company's domain will get instant access to everything Point-Free has to offer. Contact us for more info!

Monday Apr 29, 2019

Open Sourcing Enum Properties

We wanted to make Swift enum data access as ergonomic as struct data access, so today we are open sourcing a code generation tool to do just that!

Monday Mar 18, 2019

Open Sourcing Gen

Today we are open sourcing Gen: a lightweight wrapper around Swift's randomness API's that makes randomness more composable, transformable and controllable!

Tuesday Jan 8, 2019

Announcing swift-html 0.2.0

Announcing swift-html 0.2.0: support for CocoaPods, Carthage, SnapshotTesting, and more!

Wednesday Dec 19, 2018

2018 Year-in-Review

41 episodes, 19 hours of video, 25 blog posts, 8 open source libraries, 3.8K stars, 36K visitors, and we’re just getting started?

Monday Dec 3, 2018

SnapshotTesting 1.0: Delightful Swift snapshot testing

Today we are open sourcing SnapshotTesting 1.0: a modern, composable snapshot testing library built entirely in Swift!

Monday Oct 29, 2018

Some news about contramap

We've seen that contramap is a powerful operation, but the name isn't fantastic. We propose a much more intuitive name for this operation, and in doing so make our code much easier to read.

Tuesday Oct 9, 2018

How to Control the World

APIs that interact with the outside world are unpredictable and make it difficult to test and simulate code paths in our apps. Existing solutions to this problem are verbose and complicated, so let's explore a simpler solution by embracing singletons and global mutation, and rejecting protocol-oriented programming and dependency injection.

Monday Oct 8, 2018

Watch episodes in your favorite podcast app!

Follow along with the newest Point-Free episodes using your favorite podcast app. We now support podcast-friendly RSS feeds for viewing all of our videos.

Thursday Sep 20, 2018

Random Zalgo Generator

Let's create a random Zalgo text generator using the simple Gen type we defined in this week's episode!

Thursday Sep 13, 2018

Type-safe HTML with Kitura

Today we're releasing a Kitura plug-in for rendering type-safe HTML. It provides a Swift compile-time API to HTML that prevents many of the runtime errors and vulnerabilities of traditional templated HTML rendering.

Thursday Sep 13, 2018

Type-safe HTML with Vapor

Today we're releasing a Vapor plug-in for rendering type-safe HTML. It provides a Swift compile-time API to HTML that prevents many of the runtime errors and vulnerabilities of traditional templated HTML rendering.

Wednesday Sep 12, 2018

Open sourcing swift-html: A Type-Safe Alternative to Templating Languages in Swift

Today we are open sourcing a new library for building HTML documents in Swift. It's extensible, transformable, type-safe, and provides many benefits over templating languages.

Friday Aug 17, 2018

Overture 0.3.0: Now with Zip

Today we are releasing Overture 0.3.0 with a bunch of useful zip functions.

Friday Aug 17, 2018

Open Sourcing Validated

Today we are open sourcing Validated, a tiny functional Swift library for handling multiple errors: functionality that you don't get from throwing functions and the Result type.

Thursday Aug 16, 2018

Solutions to Exercises: Zip Part 3

Today we solve the exercises to the third and final part of our introductory series on zip.

Wednesday Aug 15, 2018

Solutions to Exercises: Zip Part 2

Today we solve the exercises to the second part of our introductory series on zip.

Tuesday Aug 14, 2018

Solutions to Exercises: Zip Part 1

Today we solve the exercises to the first part of our introductory series on zip.

Monday Aug 6, 2018

Announcing Student Discounts

Get 50% off your Point-Free subscription with proof of enrollment at a university or coding school.

Monday Jul 30, 2018

Celebrating 6 Months

This week marks 6 months since our launch, and we’re making one of our most popular episodes free to the public!

Monday Jul 2, 2018

Conditional Coding

What happens when we combine Swift's conditional conformance with codability?

Monday Jun 25, 2018

Open Sourcing NonEmpty

Today we are open sourcing NonEmpty, a Swift library for modeling non-empty collection types. This small library can help make your code safer and more expressive with very little work.

Monday Jun 18, 2018

Tagged Seconds and Milliseconds

Let's create a type-safe interface for dealing with seconds and milliseconds in our programs. We'll use the `Tagged` type, which allows us to construct all new types in a lightweight way.

Wednesday May 30, 2018

Styling with Functions: Free for Everyone!

We are making one of our early episodes, “UIKit Styling with Functions”, free to everyone today! It’s a seminal episode that sets the foundation for some later work in the Point-Free series.

Tuesday May 15, 2018

Overture: Now with Functional Setters

Announcing Overture 0.2.0! This release is all about setters: functions that allow us to build complex transformations out of smaller units.

Monday May 7, 2018

Solutions to Exercises: Contravariance

This week we solve the exercises from our episode on contravariance, because there were _a lot_ of them!

Monday Apr 23, 2018

Case Study: Algebraic Data Types

Let’s look at a real world use for algebraic data types. We will refactor a data type that is used in the code on this very site so that the invalid states are unrepresentable by the compiler.

Monday Apr 23, 2018

Announcing Point-Free Pointers!

Today we are excited to announcement launch of Point-Free Pointers, a blog to supplement our video series for all the content we couldn’t fit in. Expect to find regularly postings here that dive even deeper into functional programming, showing real world use cases and more!