Skip to content

Commit

Permalink
Merge branch 'master' into interviewer
Browse files Browse the repository at this point in the history
* master:
  Add @siteimprove/alfa-selective package (#702)
  Implement `Hashable` for `Either<L, R>`
  Bump typescript from 4.1.3 to 4.1.4 (#701)
  • Loading branch information
kasperisager committed Feb 10, 2021
2 parents 8786adf + 00f1263 commit 8a900c0
Show file tree
Hide file tree
Showing 13 changed files with 270 additions and 8 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ Items that are related, such as breaking changes, new features, or changes to ex

- [@siteimprove/alfa-json](packages/alfa-json): `JSON.parse()` and `JSON.stringify()` are now available.

- [@siteimprove/alfa-trampoline](packages-alfa-trampoline): `Trampoline#tee()` and `Trampoline.empty()` are now available.
- [@siteimprove/alfa-trampoline](packages/alfa-trampoline): `Trampoline#tee()` and `Trampoline.empty()` are now available.

- [@siteimprove/alfa-either](packages/alfa-either): `Either<L, R>` now provides an implementation of `Hashable`.

### Fixed

Expand Down
1 change: 1 addition & 0 deletions packages/alfa-either/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@siteimprove/alfa-equatable": "^0.10.0",
"@siteimprove/alfa-foldable": "^0.10.0",
"@siteimprove/alfa-functor": "^0.10.0",
"@siteimprove/alfa-hash": "^0.10.0",
"@siteimprove/alfa-json": "^0.10.0",
"@siteimprove/alfa-mapper": "^0.10.0",
"@siteimprove/alfa-monad": "^0.10.0",
Expand Down
6 changes: 4 additions & 2 deletions packages/alfa-either/src/either.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Equatable } from "@siteimprove/alfa-equatable";
import { Foldable } from "@siteimprove/alfa-foldable";
import { Functor } from "@siteimprove/alfa-functor";
import { Hashable } from "@siteimprove/alfa-hash";
import { Serializable } from "@siteimprove/alfa-json";
import { Mapper } from "@siteimprove/alfa-mapper";
import { Monad } from "@siteimprove/alfa-monad";
Expand All @@ -16,6 +17,7 @@ export interface Either<L, R = L>
Foldable<L | R>,
Iterable<L | R>,
Equatable,
Hashable,
Serializable<Either.JSON<L, R>> {
isLeft(): this is Left<L>;
isRight(): this is Right<R>;
Expand All @@ -40,11 +42,11 @@ export namespace Either {
return Left.isLeft(value) || Right.isRight(value);
}

export function left<L>(value: L): Either<L, never> {
export function left<L, R = never>(value: L): Either<L, R> {
return Left.of(value);
}

export function right<R>(value: R): Either<never, R> {
export function right<R, L = never>(value: R): Either<L, R> {
return Right.of(value);
}
}
12 changes: 11 additions & 1 deletion packages/alfa-either/src/left.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Equatable } from "@siteimprove/alfa-equatable";
import { Hash, Hashable } from "@siteimprove/alfa-hash";
import { Serializable } from "@siteimprove/alfa-json";
import { Mapper } from "@siteimprove/alfa-mapper";
import { None, Option } from "@siteimprove/alfa-option";
Expand Down Expand Up @@ -56,10 +57,19 @@ export class Left<L> implements Either<L, never> {
return reducer(accumulator, this._value);
}

public equals(value: unknown): value is this {
public equals<L>(value: Left<L>): boolean;

public equals(value: unknown): value is this;

public equals(value: unknown): boolean {
return value instanceof Left && Equatable.equals(value._value, this._value);
}

public hash(hash: Hash): void {
Hash.writeBoolean(hash, false);
Hashable.hash(hash, this._value);
}

public *[Symbol.iterator](): Iterator<L> {
yield this._value;
}
Expand Down
12 changes: 11 additions & 1 deletion packages/alfa-either/src/right.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Equatable } from "@siteimprove/alfa-equatable";
import { Hash, Hashable } from "@siteimprove/alfa-hash";
import { Serializable } from "@siteimprove/alfa-json";
import { Mapper } from "@siteimprove/alfa-mapper";
import { None, Option } from "@siteimprove/alfa-option";
Expand Down Expand Up @@ -56,12 +57,21 @@ export class Right<R> implements Either<never, R> {
return reducer(accumulator, this._value);
}

public equals(value: unknown): value is this {
public equals<R>(value: Right<R>): boolean;

public equals(value: unknown): value is this;

public equals(value: unknown): boolean {
return (
value instanceof Right && Equatable.equals(value._value, this._value)
);
}

public hash(hash: Hash): void {
Hash.writeBoolean(hash, true);
Hashable.hash(hash, this._value);
}

public *[Symbol.iterator](): Iterator<R> {
yield this._value;
}
Expand Down
3 changes: 3 additions & 0 deletions packages/alfa-either/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
{
"path": "../alfa-functor"
},
{
"path": "../alfa-hash"
},
{
"path": "../alfa-json"
},
Expand Down
37 changes: 37 additions & 0 deletions packages/alfa-selective/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"$schema": "http://json.schemastore.org/package",
"name": "@siteimprove/alfa-selective",
"homepage": "https://siteimprove.com",
"version": "0.10.0",
"license": "MIT",
"description": "An implementation of a selective functor for modelling conditional function application",
"repository": {
"type": "git",
"url": "https://github.com/siteimprove/alfa.git",
"directory": "packages/alfa-selective"
},
"bugs": "https://github.com/siteimprove/alfa/issues",
"main": "src/index.js",
"types": "src/index.d.ts",
"files": [
"src/**/*.js",
"src/**/*.d.ts"
],
"dependencies": {
"@siteimprove/alfa-either": "^0.10.0",
"@siteimprove/alfa-equatable": "^0.10.0",
"@siteimprove/alfa-functor": "^0.10.0",
"@siteimprove/alfa-hash": "^0.10.0",
"@siteimprove/alfa-json": "^0.10.0",
"@siteimprove/alfa-mapper": "^0.10.0",
"@siteimprove/alfa-predicate": "^0.10.0",
"@siteimprove/alfa-refinement": "^0.10.0"
},
"devDependencies": {
"@siteimprove/alfa-test": "^0.10.0"
},
"publishConfig": {
"access": "public",
"registry": "https://npm.pkg.github.com/"
}
}
1 change: 1 addition & 0 deletions packages/alfa-selective/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./selective";
103 changes: 103 additions & 0 deletions packages/alfa-selective/src/selective.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Either, Left, Right } from "@siteimprove/alfa-either";
import { Equatable } from "@siteimprove/alfa-equatable";
import { Functor } from "@siteimprove/alfa-functor";
import { Hash, Hashable } from "@siteimprove/alfa-hash";
import { Serializable } from "@siteimprove/alfa-json";
import { Mapper } from "@siteimprove/alfa-mapper";
import { Predicate } from "@siteimprove/alfa-predicate";
import { Refinement } from "@siteimprove/alfa-refinement";

export class Selective<S, T = never>
implements
Functor<T>,
Iterable<S | T>,
Equatable,
Hashable,
Serializable<Selective.JSON<S, T>> {
public static of<T>(value: T): Selective<T> {
return new Selective(Left.of(value));
}

private readonly _value: Either<S, T>;

private constructor(value: Either<S, T>) {
this._value = value;
}

public map<U>(mapper: Mapper<T, U>): Selective<S, U> {
return new Selective(
this._value.either(
(value) => Left.of(value) as Either<S, U>,
(value) => Right.of(mapper(value))
)
);
}

public if<P extends S, U>(
refinement: Refinement<S, P>,
mapper: Mapper<P, U>
): Selective<Exclude<S, P>, T | U>;

public if<U>(
predicate: Predicate<S>,
mapper: Mapper<S, U>
): Selective<S, T | U>;

public if<U>(
predicate: Predicate<S>,
mapper: Mapper<S, U>
): Selective<S, T | U> {
return this._value.either(
(value) =>
predicate(value) ? new Selective(Right.of(mapper(value))) : this,
() => this
);
}

public else<U>(mapper: Mapper<S, U>): Selective<never, T | U> {
return new Selective<never, T | U>(
Right.of(
this._value.either<T | U>(
(value) => mapper(value),
(value) => value
)
)
);
}

public get(): S | T {
return this._value.get();
}

public equals<S, T>(value: Selective<S, T>): boolean;

public equals(value: unknown): value is this;

public equals(value: unknown): boolean {
return value instanceof Selective && value._value.equals(this._value);
}

public hash(hash: Hash): void {
this._value.hash(hash);
}

public *iterator(): Iterator<S | T> {
yield this._value.get();
}

public [Symbol.iterator](): Iterator<S | T> {
return this.iterator();
}

public toJSON(): Selective.JSON<S, T> {
return this._value.toJSON();
}

public toString(): string {
return `Selective { ${this._value} }`;
}
}

export namespace Selective {
export type JSON<S, T = never> = Either.JSON<S, T>;
}
58 changes: 58 additions & 0 deletions packages/alfa-selective/test/selective.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { test } from "@siteimprove/alfa-test";

import { Refinement } from "@siteimprove/alfa-refinement";

import { Selective } from "../src/selective";

const isFoo: Refinement<string, "foo"> = (string): string is "foo" =>
string === "foo";

const isBar: Refinement<string, "bar"> = (string): string is "bar" =>
string === "bar";

test("#if() conditionally applies a function to a selective value", (t) => {
Selective.of("foo")
.if(isFoo, (value) => {
t.equal(value, "foo");
})
.if(isBar, () => {
t.fail();
});
});

test(`#else() applies a function to a selective value that matched no other
conditions`, (t) => {
Selective.of("bar")
.if(isFoo, () => {
t.fail();
})
.else((value) => {
t.equal(value, "bar");
});
});

test("#get() returns the value of a selective", (t) => {
t.equal(
Selective.of("foo")
.if(isFoo, () => "was foo")
.get(),
"was foo"
);

t.equal(
Selective.of("bar")
.if(isFoo, () => "was foo")
.get(),
"bar"
);
});

test(`#map() applies a function to a matched selective value`, (t) => {
t.equal(
Selective.of("foo")
.if(isFoo, () => "was foo")
.map((string) => string.toUpperCase())
.get(),
"WAS FOO"
);
});
34 changes: 34 additions & 0 deletions packages/alfa-selective/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"extends": "../tsconfig.json",
"files": ["src/index.ts", "src/selective.ts", "test/selective.spec.ts"],
"references": [
{
"path": "../alfa-either"
},
{
"path": "../alfa-equatable"
},
{
"path": "../alfa-functor"
},
{
"path": "../alfa-hash"
},
{
"path": "../alfa-json"
},
{
"path": "../alfa-mapper"
},
{
"path": "../alfa-predicate"
},
{
"path": "../alfa-refinement"
},
{
"path": "../alfa-test"
}
]
}
1 change: 1 addition & 0 deletions packages/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
{ "path": "alfa-rules" },
{ "path": "alfa-scraper" },
{ "path": "alfa-sarif" },
{ "path": "alfa-selective" },
{ "path": "alfa-selector" },
{ "path": "alfa-sequence" },
{ "path": "alfa-set" },
Expand Down
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9084,9 +9084,9 @@ typedarray@^0.0.6:
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=

typescript@^4.0.2:
version "4.1.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7"
integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==
version "4.1.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.4.tgz#f058636e2f4f83f94ddaae07b20fd5e14598432f"
integrity sha512-+Uru0t8qIRgjuCpiSPpfGuhHecMllk5Zsazj5LZvVsEStEjmIRRBZe+jHjGQvsgS7M1wONy2PQXd67EMyV6acg==

ua-parser-js@^0.7.21:
version "0.7.21"
Expand Down

0 comments on commit 8a900c0

Please sign in to comment.