From 5e00cdb6694463b8aaa8eb61cead388e1a431616 Mon Sep 17 00:00:00 2001 From: Xavier Cho Date: Mon, 22 Jan 2024 14:51:15 +0900 Subject: [PATCH] Refactor attribute and actor methods to return Reader The attribute and actor related methods in the code have been refactored to return a 'Reader' instead of a function. This was done across multiple files including 'attribute/bound.ts', 'attribute/attribute.ts', 'attribute/health.ts', and 'actor/actor.ts'. This change helps to handle dependencies in a functional way while allowing more composable and testable code. --- README.md | 2 +- src/actor/actor.ts | 28 +++++++++--------- src/attribute/attribute.ts | 58 ++++++++++++++++++++++---------------- src/attribute/bound.ts | 6 ++-- src/attribute/health.ts | 6 ++-- 5 files changed, 56 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index e636ea7..0c4024b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ programming paradigms, it's purely experimental at this point and not suitable f | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-97.76%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-97.77%25-brightgreen.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-87.23%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-97.76%25-brightgreen.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-97.71%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-97.77%25-brightgreen.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-87.23%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-97.71%25-brightgreen.svg?style=flat) | ## Motivation diff --git a/src/actor/actor.ts b/src/actor/actor.ts index f2a3c1f..ca2b690 100644 --- a/src/actor/actor.ts +++ b/src/actor/actor.ts @@ -8,13 +8,14 @@ import {Either} from "fp-ts/Either" import {flow} from "fp-ts/function" import * as O from "fp-ts/Option" import {Option} from "fp-ts/Option" +import {Reader} from "fp-ts/Reader" import * as A from "fp-ts/ReadonlyArray" import * as R from "fp-ts/ReadonlyRecord" import {ReadonlyRecord} from "fp-ts/ReadonlyRecord" import * as T from "io-ts" import {Mixed} from "io-ts" -import {Focusable, Identifiable, MaxLengthString, MinLengthString} from "../common" import {NameAttribute, Named, NamedData, NamedDataT} from "../attribute" +import {Focusable, Identifiable, MaxLengthString, MinLengthString} from "../common" import {UnknownActorError} from "./errors" /** @@ -142,38 +143,37 @@ export interface ActorHolder< * * @param {function} predicate A function that determines if an actor meets certain criteria. It takes a data * object as input and returns a boolean value. - * @return {function} A function that takes a context as input and returns an array of actors that satisfy + * @return {Reader} A {@link Reader} that takes a context as input and returns an array of actors that satisfy * the predicate. The returned array is read-only and cannot be modified. */ - find(predicate: (data: TData) => boolean): (context: TContext) => ReadonlyArray + find(predicate: (data: TData) => boolean): Reader> /** * Finds an actor by its unique identifier. * * @param {ActorId} id The ID of the actor to find. * - * @return {(context: TContext) => Option} A function that accepts a context and returns an option of + * @return {Reader} {Reader} A {@link Reader} that accepts a context and returns an option of * the found actor. */ - findById(id: ActorId): (context: TContext) => Option + findById(id: ActorId): Reader> /** * Returns an actor based on the given id. Returns a function that takes a context object and returns either * the resolved actor of type TActor, or an UnknownActorError if the actor could not be found. * * @param {ActorId} id The unique identifier of the actor to be resolved. - * @returns {(context: TContext) => Either} A curried function that takes a context - * object and returns an {@link Either} monad. + * @returns {Reader} {Reader} A {@link Reader} that takes a context object and returns an {@link Either} monad. */ - get(id: ActorId): (context: TContext) => Either + get(id: ActorId): Reader> } /** * Represents an abstract class for holding actors. * - * @typeparam TData The type of actor data. - * @typeparam TContext The type of actor data holder. - * @typeparam TActor The type of actor. + * @template TData The type of actor data. + * @template TContext The type of actor data holder. + * @template TActor The type of actor. */ export abstract class AbstractActorHolder< TData extends ActorData = unknown & ActorData, @@ -194,7 +194,7 @@ export abstract class AbstractActorHolder< this.get = this.get.bind(this) } - find(predicate: (data: TData) => boolean): (context: TContext) => ReadonlyArray { + find(predicate: (data: TData) => boolean): Reader> { return flow( this.optic.getOptic, @@ -207,7 +207,7 @@ export abstract class AbstractActorHolder< ) } - findById(id: ActorId): (context: TContext) => Option { + findById(id: ActorId): Reader> { return flow( this.optic.key(id).getOptic, @@ -218,7 +218,7 @@ export abstract class AbstractActorHolder< abstract findByData(data: TData): Option - get(id: ActorId): (context: TContext) => Either { + get(id: ActorId): Reader> { return flow( this.findById(id), diff --git a/src/attribute/attribute.ts b/src/attribute/attribute.ts index 7bd4531..23e6c8a 100644 --- a/src/attribute/attribute.ts +++ b/src/attribute/attribute.ts @@ -7,6 +7,7 @@ import {Optional} from "@fp-ts/optic" import * as E from "fp-ts/Either" import {Either} from "fp-ts/Either" import {flow, pipe} from "fp-ts/function" +import {Reader} from "fp-ts/Reader" import {Decoder} from "io-ts" import {PathReporter} from "io-ts/PathReporter" import {Focusable} from "../common" @@ -52,24 +53,25 @@ export interface Attribute< * Sets the value for the attribute in a given context and returns a new context. * * @param {TValue} value The value to set. - * @return {(context: TContext) => Either} A function that takes the context - * as input and returns either an error or the modified context. The possible error type is - * {@link AttributeAccessError}, or {@link ReadOnlyAttributeError}. + * @return {Reader} A {@link Reader} that takes the context as input and returns either an error or + * the modified context. The possible error type is {@link AttributeAccessError}, + * or {@link ReadOnlyAttributeError}. */ - set(value: TValue): (context: TContext) => - Either + set(value: TValue): Reader< + TContext, + Either> /** * Sets the raw (unvalidated) value in the given context. * * @param {unknown} value The value to set. - * @returns {function(context: TContext): Either} A - * function that takes a context and returns either an error or the updated context with the - * modified attribute. The possible error type is {@link AttributeAccessError}, {@link InvalidAttributeError}, - * or {@link ReadOnlyAttributeError}. + * @returns {Reader} A {@link Reader} that takes a context and returns either an error or the updated + * context with the modified attribute. The possible error type is {@link AttributeAccessError}, + * {@link InvalidAttributeError}, or {@link ReadOnlyAttributeError}. */ - setRaw(value: unknown): (context: TContext) => - Either + setRaw(value: unknown): Reader< + TContext, + Either> /** * Modifies the attribute in a context using a given function that takes a value and returns a @@ -77,11 +79,12 @@ export interface Attribute< * * @param {function} f The function that takes a value and returns a modified value. * The function should conform to the signature: `(value: TValue) => TValue` - * @returns {function} A function that takes a context and returns either an error or the modified context. + * @returns {Reader} A {@link Reader} that takes a context and returns either an error or the modified context. * The possible error type is {@link AttributeAccessError}, or {@link ReadOnlyAttributeError}. */ - modify(f: (value: TValue) => TValue): (context: TContext) => - Either + modify(f: (value: TValue) => TValue): Reader< + TContext, + Either> /** * Modifies the attribute in a context using a given function that takes a value and returns an @@ -89,12 +92,13 @@ export interface Attribute< * * @param {function} f The function that takes a value and returns a raw modified value. * The function should conform to the signature: `(value: TValue) => TValue` - * @returns {function} A function that takes a context and returns either an error or the modified context. + * @returns {Reader} A {@link Reader} that takes a context and returns either an error or the modified context. * The possible error type is {@link AttributeAccessError}, {@link InvalidAttributeError}, or * {@link ReadOnlyAttributeError}. */ - modifyRaw(f: (value: TValue) => unknown): (context: TContext) => - Either + modifyRaw(f: (value: TValue) => unknown): Reader< + TContext, + Either> } /** @@ -216,8 +220,9 @@ export abstract class AbstractAttribute< ) } - set(value: TData[TName]): (context: TContext) => - Either { + set(value: TData[TName]): Reader< + TContext, + Either> { return flow( this.optic.setOptic(value), @@ -226,8 +231,9 @@ export abstract class AbstractAttribute< ) } - setRaw(value: unknown): (context: TContext) => - Either { + setRaw(value: unknown): Reader< + TContext, + Either> { return context => pipe( this.validate(value), @@ -236,8 +242,9 @@ export abstract class AbstractAttribute< ) } - modify(f: (value: TData[TName]) => TData[TName]): (context: TContext) => - Either { + modify(f: (value: TData[TName]) => TData[TName]): Reader< + TContext, + Either> { return (context: TContext) => pipe( this.get(context), @@ -246,8 +253,9 @@ export abstract class AbstractAttribute< ) } - modifyRaw(f: (value: TData[TName]) => unknown): (context: TContext) => - Either { + modifyRaw(f: (value: TData[TName]) => unknown): Reader< + TContext, + Either> { return context => pipe( this.get(context), diff --git a/src/attribute/bound.ts b/src/attribute/bound.ts index 08ba889..6859ade 100644 --- a/src/attribute/bound.ts +++ b/src/attribute/bound.ts @@ -9,6 +9,7 @@ import {pipe} from "fp-ts/function" import * as O from "fp-ts/Option" import {Option} from "fp-ts/Option" import {Ord} from "fp-ts/Ord" +import {Reader} from "fp-ts/Reader" import {clamp, Range} from "../common" import {AbstractAttribute, Attribute, AttributeOptions} from "./attribute" import {AttributeAccessError, ReadOnlyAttributeError} from "./errors" @@ -100,8 +101,9 @@ export abstract class AbstractBoundAttribute< ) } - override set(value: TData[TName]): (context: TContext) => - Either { + override set(value: TData[TName]): Reader< + TContext, + Either> { return context => pipe( this.getRange(context), diff --git a/src/attribute/health.ts b/src/attribute/health.ts index 7953c9e..5ad0da8 100644 --- a/src/attribute/health.ts +++ b/src/attribute/health.ts @@ -1,5 +1,6 @@ import {Optional} from "@fp-ts/optic" import {Either} from "fp-ts/Either" +import {Reader} from "fp-ts/Reader" import * as T from "io-ts" import {NonNegativeInt} from "io-ts-numbers" import {withMessage} from "io-ts-types" @@ -42,8 +43,9 @@ export class HealthAttribute export function damage( target: Damageable, amount: Health -): (context: TContext) => - Either { +): Reader< + TContext, + Either> { return target.health.modifyRaw(v => v - amount) }