Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @siteimprove/alfa-selective package #702

Merged
merged 2 commits into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>,
kasperisager marked this conversation as resolved.
Show resolved Hide resolved
(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 @@ -78,6 +78,7 @@
{ "path": "alfa-rules" },
{ "path": "alfa-scraper" },
{ "path": "alfa-sarif" },
{ "path": "alfa-selective" },
{ "path": "alfa-selector" },
{ "path": "alfa-sequence" },
{ "path": "alfa-set" },
Expand Down