Skip to content
This repository has been archived by the owner on Sep 15, 2024. It is now read-only.

Commit

Permalink
Refactor attribute and actor methods to return Reader
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mysticfall committed Jan 22, 2024
1 parent 88382b1 commit 5e00cdb
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 44 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
28 changes: 14 additions & 14 deletions src/actor/actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"

/**
Expand Down Expand Up @@ -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<TActor>
find(predicate: (data: TData) => boolean): Reader<TContext, ReadonlyArray<TActor>>

/**
* Finds an actor by its unique identifier.
*
* @param {ActorId} id The ID of the actor to find.
*
* @return {(context: TContext) => Option<TActor>} 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<TActor>
findById(id: ActorId): Reader<TContext, Option<TActor>>

/**
* 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<UnknownActorError, TActor>} 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<UnknownActorError, TActor>
get(id: ActorId): Reader<TContext, Either<UnknownActorError, TActor>>
}

/**
* 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,
Expand All @@ -194,7 +194,7 @@ export abstract class AbstractActorHolder<
this.get = this.get.bind(this)
}

find(predicate: (data: TData) => boolean): (context: TContext) => ReadonlyArray<TActor> {
find(predicate: (data: TData) => boolean): Reader<TContext, ReadonlyArray<TActor>> {

return flow(
this.optic.getOptic,
Expand All @@ -207,7 +207,7 @@ export abstract class AbstractActorHolder<
)
}

findById(id: ActorId): (context: TContext) => Option<TActor> {
findById(id: ActorId): Reader<TContext, Option<TActor>> {

return flow(
this.optic.key(id).getOptic,
Expand All @@ -218,7 +218,7 @@ export abstract class AbstractActorHolder<

abstract findByData(data: TData): Option<TActor>

get(id: ActorId): (context: TContext) => Either<UnknownActorError, TActor> {
get(id: ActorId): Reader<TContext, Either<UnknownActorError, TActor>> {

return flow(
this.findById(id),
Expand Down
58 changes: 33 additions & 25 deletions src/attribute/attribute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -52,49 +53,52 @@ 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<ReadOnlyAttributeError, TContext>} 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<AttributeAccessError | ReadOnlyAttributeError, TContext>
set(value: TValue): Reader<
TContext,
Either<AttributeAccessError | ReadOnlyAttributeError, TContext>>

/**
* Sets the raw (unvalidated) value in the given context.
*
* @param {unknown} value The value to set.
* @returns {function(context: TContext): Either<InvalidAttributeError | ReadOnlyAttributeError, TContext>} 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<AttributeAccessError | InvalidAttributeError | ReadOnlyAttributeError, TContext>
setRaw(value: unknown): Reader<
TContext,
Either<AttributeAccessError | InvalidAttributeError | ReadOnlyAttributeError, TContext>>

/**
* Modifies the attribute in a context using a given function that takes a value and returns a
* modified value.
*
* @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<AttributeAccessError | ReadOnlyAttributeError, TContext>
modify(f: (value: TValue) => TValue): Reader<
TContext,
Either<AttributeAccessError | ReadOnlyAttributeError, TContext>>

/**
* Modifies the attribute in a context using a given function that takes a value and returns an
* unvalidated modified value.
*
* @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<AttributeAccessError | InvalidAttributeError | ReadOnlyAttributeError, TContext>
modifyRaw(f: (value: TValue) => unknown): Reader<
TContext,
Either<AttributeAccessError | InvalidAttributeError | ReadOnlyAttributeError, TContext>>
}

/**
Expand Down Expand Up @@ -216,8 +220,9 @@ export abstract class AbstractAttribute<
)
}

set(value: TData[TName]): (context: TContext) =>
Either<AttributeAccessError | ReadOnlyAttributeError, TContext> {
set(value: TData[TName]): Reader<
TContext,
Either<AttributeAccessError | ReadOnlyAttributeError, TContext>> {

return flow(
this.optic.setOptic(value),
Expand All @@ -226,8 +231,9 @@ export abstract class AbstractAttribute<
)
}

setRaw(value: unknown): (context: TContext) =>
Either<AttributeAccessError | InvalidAttributeError | ReadOnlyAttributeError, TContext> {
setRaw(value: unknown): Reader<
TContext,
Either<AttributeAccessError | InvalidAttributeError | ReadOnlyAttributeError, TContext>> {

return context => pipe(
this.validate(value),
Expand All @@ -236,8 +242,9 @@ export abstract class AbstractAttribute<
)
}

modify(f: (value: TData[TName]) => TData[TName]): (context: TContext) =>
Either<AttributeAccessError | ReadOnlyAttributeError, TContext> {
modify(f: (value: TData[TName]) => TData[TName]): Reader<
TContext,
Either<AttributeAccessError | ReadOnlyAttributeError, TContext>> {

return (context: TContext) => pipe(
this.get(context),
Expand All @@ -246,8 +253,9 @@ export abstract class AbstractAttribute<
)
}

modifyRaw(f: (value: TData[TName]) => unknown): (context: TContext) =>
Either<AttributeAccessError | InvalidAttributeError | ReadOnlyAttributeError, TContext> {
modifyRaw(f: (value: TData[TName]) => unknown): Reader<
TContext,
Either<AttributeAccessError | InvalidAttributeError | ReadOnlyAttributeError, TContext>> {

return context => pipe(
this.get(context),
Expand Down
6 changes: 4 additions & 2 deletions src/attribute/bound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -100,8 +101,9 @@ export abstract class AbstractBoundAttribute<
)
}

override set(value: TData[TName]): (context: TContext) =>
Either<AttributeAccessError | ReadOnlyAttributeError, TContext> {
override set(value: TData[TName]): Reader<
TContext,
Either<AttributeAccessError | ReadOnlyAttributeError, TContext>> {

return context => pipe(
this.getRange(context),
Expand Down
6 changes: 4 additions & 2 deletions src/attribute/health.ts
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -42,8 +43,9 @@ export class HealthAttribute<TContext = unknown>
export function damage<TContext>(
target: Damageable<TContext>,
amount: Health
): (context: TContext) =>
Either<AttributeAccessError | InvalidAttributeError | ReadOnlyAttributeError, TContext> {
): Reader<
TContext,
Either<AttributeAccessError | InvalidAttributeError | ReadOnlyAttributeError, TContext>> {

return target.health.modifyRaw(v => v - amount)
}

0 comments on commit 5e00cdb

Please sign in to comment.