fp-ts icon indicating copy to clipboard operation
fp-ts copied to clipboard

extended Bounded type

Open jessekelly881 opened this issue 3 years ago • 8 comments

Extends Bounded type. It's currently a work in progress, but I am looking for feedback. The thinking with these functions is that Bounded effectively represents a range of values. So, testing whether a value is within a range, if ranges overlap, clamping a value to a range, etc., makes sense.

import * as n from 'fp-ts/number';
import { top, bottom, fromRange, isWithin, clamp, toTuple } from 'fp-ts/Bounded';

const bound = fromRange(n.Ord)(0)(10);

top(bound) // 0
bottom(bound) //10

isWithin(bound)(1) //true
isWithin(bound)(11) //false

toTuple(bound) // [0, 10]

clamp(bound)(12) // 10

jessekelly881 avatar Jul 26 '21 20:07 jessekelly881

Fixed issues.

jessekelly881 avatar Aug 02 '21 23:08 jessekelly881

Looks good. Just another idea when creating a Bound why not have a function that always returns a valid bound like your fromRange but one that never fails.

export const bounded = <T>(O: Ord.Ord<T>) => (b: T) => (t: T): Bounded<T> =>
  Ord.geq(O)(B.bottom, B.top) ? ({ ...O, bottom: b, top: t }) : ({ ...O, bottom: t, top: b })

What do you think?

mlegenhausen avatar Aug 03 '21 07:08 mlegenhausen

Thanks! Hmm. This seems useful; although, I can't quite imagine a use case for it at the moment. Maybe called something like coerceBound to clarify that the order might not be the same as the order of the provided arguments?

jessekelly881 avatar Aug 03 '21 16:08 jessekelly881

Added coerceBound, reverse, and isEmpty. Although, isEmpty might not be the best name for this function as a bound where top === bottom has exactly one value that is within the bound. Any thoughts? I'm leaning towards isSingular, isFlat, or isClosed.

jessekelly881 avatar Aug 03 '21 18:08 jessekelly881

Use case for me would be as smart constructor where I do not care about validation and just want a valid Bounded.

I find isSingular a good choice.

mlegenhausen avatar Aug 04 '21 08:08 mlegenhausen

Ok. I like the idea of a smart constructor. And isSingular is a good name. I updated the pr. Is there anything else that you can think of that needs changing?

jessekelly881 avatar Aug 05 '21 03:08 jessekelly881

Fixed.

jessekelly881 avatar Aug 05 '21 19:08 jessekelly881

Hmm. Any idea what might be up with the automated testing? It seems like an issue with prettier but running prettier --write doesn't fix the issue and the error message isn't specific enough. Also, I'm considering adding a function that constructs a Bounded type from an instance of Ord and a ReadonlyNonEmptyArray of values. What are your thoughts? Something like this:

import * as A from 'fp-ts/ReadonlyNonEmptyArray';
import * as T from 'fp-ts/Tuple';
import * as n from 'fp-ts/number';
import { Bounded } from 'fp-ts/Bounded';
import { Ord } from 'fp-ts/Ord';
import { pipe } from 'fp-ts/function';

const fromArray = <X>(ord: Ord<X>) => (arr: A.ReadonlyNonEmptyArray<X>) => pipe(
    arr,
    x => [x, x] as [A.ReadonlyNonEmptyArray<X>, A.ReadonlyNonEmptyArray<X>],
    T.bimap(A.max(ord), A.min(ord)), // [min, max]
    t => ({ ...ord, bottom: t[0], top: t[1] }) as Bounded<X>
)

// Returns instance of Bounded where { bottom: 1, top: 5 }
fromArray(n.Ord)([1, 2, 3, 4, 5])

jessekelly881 avatar Aug 10 '21 16:08 jessekelly881