functir

所属分类:前端开发
开发工具:TypeScript
文件大小:0KB
下载次数:0
上传日期:2024-03-30 19:00:19
上 传 者sh-1993
说明:  JavaScript的函数编程库。全类型安全。
(Functional programming library for JavaScript. Fully type-safe.)

文件列表:
.vscode/
src/
LICENSE
jest.config.js
nodemon.json
package.json
pnpm-lock.yaml
tsconfig.json

# About _Functir_ is a functional programming library for JavaScript. True functional programming in JS! ### Installation - [Installation](https://github.com/qrailibs/functir/blob/master/#installation) ### Core data types - [`Box`](https://github.com/qrailibs/functir/blob/master/#box) - [`Option` (`None`, `Some`)](https://github.com/qrailibs/functir/blob/master/#option) - [`Either` (`Left`, `Right`)](https://github.com/qrailibs/functir/blob/master/#either) - [`LikeBox`](https://github.com/qrailibs/functir/blob/master/#likebox) - [`Seq`](https://github.com/qrailibs/functir/blob/master/#seq) - [`Trait`](https://github.com/qrailibs/functir/blob/master/#trait) - [`IO`, `AsyncIO`](https://github.com/qrailibs/functir/blob/master/#io) - [`Throwable`](https://github.com/qrailibs/functir/blob/master/#throwable) - [`Action`](https://github.com/qrailibs/functir/blob/master/#action) ### Core features - [Pattern matching](https://github.com/qrailibs/functir/blob/master/#pattern-matching) - [Piping](https://github.com/qrailibs/functir/blob/master/#piping) # Installation via pnpm: ```bash pnpm add functir ``` via npm: ```bash npm i functir ``` # Core data types ## `Box` Box is super simple – it's class that containing some immutable value. You cannot change box wrapped value directly, but you can transform it and get new Box instance. ```ts import { Box } from 'functir' // 1. Let's create wrapped number value const wrapped = new Box(1) console.log(wrapped.value) // 1 // 2. Let's change it (immutable!) const wrappedTransformed = wrapped.mutate(_ => _ + 100) console.log(wrappedTransformed.value) // 101 ``` Other types described in future also will also use `Box` to contain some value via class construction. ## `Option` Mostly `Option` data type is used in pattern matching (described in future). `Option` is primary designed to be one of two values: `None` or `Some(value)`. `Some` value is works same as `Box` – wraps some value. ```ts import { Option, None, Some } from 'functir' // Both of values is Option const valueOne: Option = None() const valueTwo: Option = Some(200) // You can mutate value of `Some` by converting to `Box` console.log(valueTwo.asBox) // Box(200) console.log(valueTwo.asBox.mutate(_ => _ + 5)) // Box(205) ``` ## `Either` The `Either` is very similar to `Option`, but the value can be `Left(value: TLeft)` or `Right(value: TRight)`: ```ts import { Either, Left, Right } from 'functir' // Either = Left | Right // Both of values is Either const valueOne: Either = Left(100) const valueTwo: Either = Right('200') // You can mutate value by converting to `Box` console.log(valueOne.asBox) // Box(100) console.log(valueOne.asBox.mutate(_ => _ + 5)) // Box(105) ``` ## `LikeBox` What does `Box`, `Option`, `Either` have in common? - they are all implements `LikeBox` interface, but in different variations. We have 3 main variations of `LikeBox` interface: - `LikeBox` (`None` type uses it, it doesn't have wrapped value inside) - `LikeFilledBox` (`Box` type uses it, inherits `LikeBox` but have wrapped value inside) - `LikeConvertibleFilledBox` (`Option` & `Either` types uses it, inherits `LikeFilledBox` but have `asBox` converter inside) How you can use those interfaces? Like that: ```ts import { Box } from 'functir' class SomeValue extends Box.filled {} const wrapped = new SomeValue(200) console.log(wrapped.value) // 200 console.log(wrapped.asBox) // Box(200) ``` Illustrated code produces for you a `LikeConvertibleFilledBox` class with wrapped `number`, that you can use. The same thing does `None`, `Some`, `Left`, `Right` implementation. Now let's see what functions does `LikeBox` provides: ### Usage: `match`/`pipe` Shorthand for using pattern matching or piping: ```ts import { Option, None, Some, match, is } from 'functir' const boxNone: Option = None() const boxSome: Option = Some(200) // `.match`/`.pipe` – this functions is works on // `Box`, `Option`, `Either`, `Box.filled` // becase they are `LikeBox` implementations // Pattern matching boxNone.match([ is(None, _ => "none"), is(Some, _ => "some") ]) // none boxSome.match([ is(None, _ => "none"), is(Some, _ => "some") ]) // some // Piping boxSome.pipe([ _ => _ + 100, // 200 + 100 = 300 _ => _ + 50 // 300 + 50 = 350 ]) // 350 ``` ### Usage: `flatten` This magic function is just flattens wrapped `LikeBox` values: ```ts import { Box, Some } from 'functir' // `Box` inside `Box` nested const deep = new Box(new Box(5)) console.log(deep.flatten()) // 5 // Different `LikeBox` implementations nested const complexDeep = new Box(new Some(new Box(new Some(20)))) console.log(complexDeep.flatten()) // 20 ``` ## `Seq` `Seq` (Sequence) is helpful data type that you can use as alternative for arrays. Why `Seq` instead of arrays? 1. `Seq` is fully immutable-safe (doesn't provides any mutable methods) 2. `Seq` provides a lot of methods that arrays doesn't Usage is simple: ```ts import { Seq } from 'functir' const seq = new Seq(1, 2, 3) console.log(seq.asArray) // [1, 2, 3] ``` ## Usage: methods `Seq` provides many different immutable methods you can use: ```ts // Just copies current sequence seq.copy() // Seq(1, 2, 3) ``` ```ts // Converting into array, set, map seq.asArray // [1, 2, 3] seq.asSet // Set(1, 2, 3) seq.asMap // Map({ 0: 1, 1: 2, 2: 3 }) ``` ```ts // Adds value to start of seq seq.prepended(10) // Seq(10, 1, 2, 3) // Adds value to end of seq seq.appended(10) // Seq(1, 2, 3, 10) ``` ```ts // Auto sorting, like [].sort() seq.autoSorted(0, 10) // Seq(1, 2, 3) // Sort using predicate seq.sorted((a, b) => (a > b ? -1 : 1)) // Seq(3, 2, 1) ``` ```ts // Reverses values seq.reversed() // Seq(3, 2, 1) ``` ```ts // Maps values seq.mapped(_ => _ + 10) // Seq(11, 12, 13) ``` ```ts // Filters values seq.filtered(_ => _ > 1) // Seq(2, 3) ``` ```ts // Pads from start with value (to length of 6) seq.padStart(6, -1) // Seq(-1, -1, -1, 1, 2, 3) // Pads from end with value (to length of 6) seq.padEnd(6, -1) // Seq(1, 2, 3, -1, -1, -1) ``` ```ts // Get index of item (from start) new Seq(1, 1, 1).indexOf(1) // 0 // Get index of item (from end) new Seq(1, 1, 1).lastIndexOf(1) // 2 ``` ## `Trait` Trait is the concept from `Scala`/`Rust` languages. Using `Trait` you can easily create class (*DTO/DAO*) for some data model: ```ts import { Trait } from 'functir' // Create a class using trait // Trait will automatically create immutable fields, constructor for the class const UserDTO = Trait<{ readonly nickname: string readonly age: number }>(); // Create instance of trait // All fields of trait should be passed to constructor const jake = new UserDTO({ nickname: 'Jake', age: 20 }) // You can convert trait instances into objects or `Box` console.log(jake.asObject) // { nickname: 'Jake', age: 20 } console.log(jake.asBox) // Box({ nickname: 'Jake', age: 20 ) ``` `Trait` of course produces immutable classes (with `copy` method), you can use that for creating services: ```ts import { Trait, Seq } from 'functir' /** * Immutable service that builds pizza */ class PizzaService extends Trait<{ readonly size: 'sm' | 'md' | 'lg'; readonly toppings: Seq<'meat' | 'pineapple' | 'cheese'>; }>() { constructor() { super({ size: 'sm', toppings: new Seq() }) } public setSize = (size: 'sm' | 'md' | 'lg') => this.copy({ size }) public addTopping = (topping: 'meat' | 'pineapple' | 'cheese') => this.copy({ toppings: this.toppings.appended(topping) }) } // Immutability! Let's build pizza const myPizza = new PizzaService() .setSize('md') .addTopping('meat') .addTopping('cheese') .asObject console.log(myPizza) // { size: 'md', toppings: ['meat', 'cheese'] } ``` ## `IO` `IO` is also another helpful type for defining input and output of function: ```ts import { IO } from 'functir' // Operation that converts string to number const toNumber: IO = _ => parseInt(_) console.log(toNumber('123')) // 123 ``` Also if function is async you can use `AsyncIO`: ```ts import { AsyncIO } from 'functir' // Operation that async (just return input as output) const someFunction: AsyncIO = async _ => _ ``` There's third parameter in IO that we didn't mentioned, it's used to define what `Throwable` (described below) can be given by function: ```ts import { IO, ThrowableTrait } from 'functir' // Our own throwable error class TooLongError extends ThrowableTrait('MyOwnError', 'Value was too long') {} // Our operation described: // input = string, output = string, throws = MyOwnError // if input length > 5 we return it, otherwise TooLongError given const someFunction: IO = _ => _.length > 5 ? _ : new TooLongError ``` ## `Throwable` The `Throwable` is the helpful type used with `IO` to annotate what error can happen in a function: ```ts import { Throwable, ThrowableTrait } from 'functir' // Simple error const someError: Throwable = new Error('Some custom throwed error') ``` Also you can create own throwable error class using `ThrowableTrait`: ```ts import { Throwable, ThrowableTrait } from 'functir' // Create own throwable error class CustomError extends ThrowableTrait('CustomError') {} // Our custom error const someError2: Throwable = new CustomError('Some super custom error') ``` # Core features ## Pattern matching Pattern matching if well-known pattern that allows to match value to a different cases like in a `switch-case`, but more clean syntax and ability to check for instances of classes. Example: ```ts import { match, is, _, Some, None } from 'functir' // 1 if Some // 0 if None // -1 otherwise const result = match(Some(100))([ is(Some, _ => 1), is(None, _ => 0), is(_, _ => -1) ]) console.log(result) // 1 ``` The `_` is special symbol used to handle case none of cases is matched. ## Piping Will be described later.

近期下载者

相关文件


收藏者