All Projects → pointfreeco → Swift Validated

pointfreeco / Swift Validated

Licence: mit
🛂 A result type that accumulates multiple errors.

Programming Languages

swift
15916 projects

Projects that are alternatives of or similar to Swift Validated

Hamsters
A mini Scala utility library
Stars: ✭ 292 (-16.57%)
Mutual labels:  error-handling, validation, functional-programming
Deal
Design by contract for Python with static checker and tests' generation.
Stars: ✭ 164 (-53.14%)
Mutual labels:  validation, functional-programming
Faux Pas
A library that simplifies error handling for Functional Programming in Java
Stars: ✭ 100 (-71.43%)
Mutual labels:  error-handling, functional-programming
Bash Oo Framework
Bash Infinity is a modern standard library / framework / boilerplate for Bash
Stars: ✭ 5,247 (+1399.14%)
Mutual labels:  error-handling, functional-programming
Vue Formulate
⚡️ The easiest way to build forms with Vue.
Stars: ✭ 1,947 (+456.29%)
Mutual labels:  error-handling, validation
Poica
🧮 A research programming language on top of C macros
Stars: ✭ 231 (-34%)
Mutual labels:  error-handling, functional-programming
Deep Waters
🔥Deep Waters is an easy-to-compose functional validation system for javascript developers 🔥
Stars: ✭ 188 (-46.29%)
Mutual labels:  validation, functional-programming
Framework
Strongly-typed JavaScript object with support for validation and error handling.
Stars: ✭ 136 (-61.14%)
Mutual labels:  error-handling, validation
validator
💯Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
Stars: ✭ 9,721 (+2677.43%)
Mutual labels:  validation, error-handling
Fluentresults
A generalised Result object implementation for .NET/C#
Stars: ✭ 266 (-24%)
Mutual labels:  error-handling, validation
Swiftz
Functional programming in Swift
Stars: ✭ 3,327 (+850.57%)
Mutual labels:  functional-programming
Joi
The most powerful data validation library for JS
Stars: ✭ 17,989 (+5039.71%)
Mutual labels:  validation
Openapi Cop
A proxy that validates responses and requests against an OpenAPI document.
Stars: ✭ 338 (-3.43%)
Mutual labels:  validation
Hazel
Hazel, a live functional programming environment with typed holes
Stars: ✭ 340 (-2.86%)
Mutual labels:  functional-programming
Tslog
📝 tslog - Expressive TypeScript Logger for Node.js.
Stars: ✭ 321 (-8.29%)
Mutual labels:  error-handling
Data Structures
Go datastructures.
Stars: ✭ 336 (-4%)
Mutual labels:  error-handling
Magic In Ten Mins
十分钟魔法练习
Stars: ✭ 327 (-6.57%)
Mutual labels:  functional-programming
Here Be Dragons
An Intellij/Android Studio plugin to help visualise side effects in your code.
Stars: ✭ 325 (-7.14%)
Mutual labels:  functional-programming
Effect
effect isolation in Python, to facilitate more purely functional code
Stars: ✭ 324 (-7.43%)
Mutual labels:  functional-programming
Functionaltabledata
Declarative UITableViewDataSource implementation
Stars: ✭ 347 (-0.86%)
Mutual labels:  functional-programming

🛂 Validated

Swift 5.1 CI @pointfreeco

A result type that accumulates multiple errors.

Table of Contents

Motivation

The problem

Swift error handling short-circuits on the first failure. Because of this, it's not the greatest option for handling things like form data, where multiple inputs may result in multiple errors.

struct User {
  let id: Int
  let email: String
  let name: String
}

func validate(id: Int) throws -> Int {
  guard id > 0 else {
    throw Invalid.error("id must be greater than zero")
  }
  return id
}

func validate(email: String) throws -> String {
  guard email.contains("@") else {
    throw Invalid.error("email must be valid")
  }
  return email
}

func validate(name: String) throws -> String {
  guard !name.isEmpty else {
    throw Invalid.error("name can't be blank")
  }
  return name
}

func validateUser(id: Int, email: String, name: String) throws -> User {
  return User(
    id: try validate(id: id),
    email: try validate(id: email),
    name: try validate(id: name)
  )
}

Here we've combined a few throwing functions into a single throwing function that may return a User.

let user = try validateUser(id: 1, email: "[email protected]", name: "Blob")
// User(id: 1, email: "[email protected]", name: "Blob")

If the id, email, or name are invalid, an error is thrown.

let user = try validateUser(id: 1, email: "[email protected]", name: "")
// throws Invalid.error("name can't be blank")

Unfortunately, if several or all of these inputs are invalid, the first error wins.

let user = try validateUser(id: -1, email: "blobpointfree.co", name: "")
// throws Invalid.error("id must be greater than zero")

Handling multiple errors with Validated

Validated is a Result-like type that can accumulate multiple errors. Instead of using throwing functions, we can define functions that work with Validated.

func validate(id: Int) -> Validated<Int, String> {
  return id > 0
    ? .valid(id)
    : .error("id must be greater than zero")
}

func validate(email: String) -> Validated<String, String> {
  return email.contains("@")
    ? .valid(email)
    : .error("email must be valid")
}

func validate(name: String) -> Validated<String, String> {
  return !name.isEmpty
    ? .valid(name)
    : .error("name can't be blank")
}

To accumulate errors, we use a function that we may already be familiar with: zip.

let validInputs = zip(
  validate(id: 1),
  validate(email: "[email protected]"),
  validate(name: "Blob")
)
// Validated<(Int, String, String), String>

The zip function on Validated works much the same way it works on sequences, but rather than zipping a pair of sequences into a sequence of pairs, it zips up a group of single Validated values into single Validated value of a group.

From here, we can use another function that we may already be familiar with, map, which takes a transform function and produces a new Validated value with its valid case transformed.

let validUser = validInputs.map(User.init)
// valid(User(id: 1, email: "[email protected]", name: "Blob"))

Out group of valid inputs has transformed into a valid user.

For ergonomics and composition, a curried zip(with:) function is provided that takes both a transform function and Validated inputs.

zip(with: User.init)(
  validate(id: 1),
  validate(email: "[email protected]"),
  validate(name: "Blob")
)
// valid(User(id: 1, email: "[email protected]", name: "Blob"))

An invalid input yields an error in the invalid case.

zip(with: User.init)(
  validate(id: 1),
  validate(email: "[email protected]"),
  validate(name: "")
)
// invalid(["name can't be blank"])

More importantly, multiple invalid inputs yield an invalid case with multiple errors.

zip(with: User.init)(
  validate(id: -1),
  validate(email: "blobpointfree.co"),
  validate(name: "")
)
// invalid([
//   "id must be greater than zero",
//   "email must be valid",
//   "name can't be blank"
// ])

Invalid errors are held in a non-empty array to provide a compile-time guarantee that you will never encounter an empty invalid case.

Installation

Carthage

If you use Carthage, you can add the following dependency to your Cartfile:

github "pointfreeco/swift-validated" ~> 0.2.1

CocoaPods

If your project uses CocoaPods, just add the following to your Podfile:

pod 'PointFree-Validated', '~> 0.2.1'

SwiftPM

If you want to use Validated in a project that uses SwiftPM, it's as simple as adding a dependencies clause to your Package.swift:

dependencies: [
  .package(url: "https://github.com/pointfreeco/swift-validated.git", from: "0.2.1")
]

Xcode Sub-project

Submodule, clone, or download Validated, and drag Validated.xcodeproj into your project.

Interested in learning more?

These concepts (and more) are explored thoroughly in Point-Free, a video series exploring functional programming and Swift hosted by Brandon Williams and Stephen Celis.

Validated was explored in The Many Faces of Zip: Part 2:

video poster image

License

All modules are released under the MIT license. See LICENSE for details.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].