Giter Club home page Giter Club logo

data's Introduction

Effect

Welcome to Effect, a powerful TypeScript framework that provides a fully-fledged functional effect system with a rich standard library.

Requirements

  • TypeScript 5.4 or newer
  • The strict flag enabled in your tsconfig.json file
{
  // ...
  "compilerOptions": {
    // ...
    "strict": true,
  },
}

Documentation

For detailed information and usage examples, please visit the Effect website.

Introduction to Effect

To get started with Effect, watch our introductory video on YouTube. This video provides an overview of Effect and its key features, making it a great starting point for newcomers:

Introduction to Effect

Connect with Our Community

Join our vibrant community on Discord to interact with fellow developers, ask questions, and share your experiences. Here's the invite link to our Discord server: Join Effect's Discord Community.

API Reference

For detailed information on the Effect API, please refer to our API Reference.

Contributing via Pull Requests

We welcome contributions via pull requests! Here are some guidelines to help you get started:

Setting Up Your Environment

Begin by forking the repository and clone it to your local machine.

Navigate into the cloned repository and create a new branch for your changes:

git checkout -b my-branch

Ensure all required dependencies are installed by running:

pnpm install  # Requires pnpm version 9.0.4

Making Changes

Implement Your Changes

Make the changes you propose to the codebase. If your changes impact functionality, please add corresponding tests to validate your updates.

Validate Your Changes

Run the following commands to ensure your changes do not introduce any issues:

  • pnpm codegen (optional): Re-generate the package entrypoints in case you have changed the structure of a package or introduced a new module.
  • pnpm check: Confirm that the code compiles without errors.
  • pnpm test: Execute all unit tests to ensure your changes haven't broken existing functionality.
  • pnpm circular: Check for any circular dependencies in imports.
  • pnpm lint: Ensure the code adheres to our coding standards.
    • If you encounter style issues, use pnpm lint-fix to automatically correct some of these.
  • pnpm dtslint: Run type-level tests.
  • pnpm docgen: Ensure the documentation generates correctly and reflects any changes made.

Document Your Changes

JSDoc Comments

When adding a new feature, it's important to document your code using JSDoc comments. This helps other developers understand the purpose and usage of your changes. Include at least the following in your JSDoc comments:

  • A Short Description: Summarize the purpose and functionality of the feature.
  • Example: Provide a usage example under the @example tag to demonstrate how to use the feature.
  • Since Version: Use the @since tag to indicate the version in which the feature was introduced. If you're unsure about the version, please consult with a project maintainer.
  • Category (Optional): You can categorize the feature with the @category tag to help organize the documentation. If you're unsure about what category to assign, ask a project maintainer.

Changeset Documentation

Before committing your changes, document them with a changeset. This process helps in tracking modifications and effectively communicating them to the project team and users:

pnpm changeset

During the changeset creation process, you will be prompted to select the appropriate level for your changes:

  • patch: Opt for this if you are making small fixes or minor changes that do not affect the library's overall functionality.
  • minor: Choose this for new features that enhance functionality but do not disrupt existing features.
  • major: Select this for any changes that result in backward-incompatible modifications to the library.

Finalizing Your Contribution

Commit Your Changes

Once you have documented your changes with a changeset, it’s time to commit them to the repository. Use a clear and descriptive commit message, which could be the same message you used in your changeset:

git commit -am 'Add some feature'

Linking to Issues

If your commit addresses an open issue, reference the issue number directly in your commit message. This helps to link your contribution clearly to specific tasks or bug reports. Additionally, if your commit resolves the issue, you can indicate this by adding a phrase like ", closes #<issue-number>". For example:

git commit -am 'Add some feature, closes #123'

This practice not only helps in tracking the progress of issues but also automatically closes the issue when the commit is merged, streamlining project management.

Push to Your Fork

Push the changes up to your GitHub fork:

git push origin my-branch

Create a Pull Request

Open a pull request against the appropriate branch on the original repository:

  • main branch: For minor patches or bug fixes.
  • next-minor branch: For new features that are non-breaking.
  • next-major branch: For changes that introduce breaking modifications.

Please be patient! We will do our best to review your pull request as soon as possible.

data's People

Contributors

0x706b avatar fizzyelt avatar fubhy avatar gabeatwork avatar gcanti avatar github-actions[bot] avatar iamchanii avatar imax153 avatar jessekelly881 avatar khraksmamtsov avatar mikearnaldi avatar patroza avatar pete-murphy avatar rarebodhi avatar sukovanej avatar tatchi avatar tim-smart avatar tstelzer avatar tylors avatar vecerek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

data's Issues

Port `flow` from fp-ts

I've recently experienced the lack of flow in Effect:

import { pipe } from "effect";
const double = (x: number): number => x * 2;
const add = (x: number) => (y: number): number => x + y;

pipe(
  Effect.succeed(20),
  // instead of: Effect.map(flow(double, add(2)))
  Effect.map((n) => pipe(n, double, add(2)),
  Effect.runSync
)
// 42

May I copy it over from fp-ts, please? πŸ™ πŸ˜„

DurationInput

Duration.lessThan/etc functions should accept DurationInput

Struct.evolve forced type signature makes for an invalid transformation object

version: 0.12.8

You can force an invalid type signature to Struct.evolve by explicitly annotating its generics during usage.

import * as Struct from "@effect/data/Struct"

const mystruct = { a: 42, b: "hello" }
const good = Struct.evolve(mystruct, {  a: `not a function` }) // error βœ…
const bad = Struct.evolve<typeof mystruct, Record<string, string>>(mystruct, { a: `not a function` }) // not an error ❌

In the example, the "good" row is correctly reporting an error, while the "bad" one isn't while it should.
This makes the second call to Struct.evolve fail at runtime cause it's expecting a function to call and instead if finds not a function.

I can only reproduce it by forcing its generics, but it first happened to me without forcing them, in a complex scenario when I used a generic key to create a transformation object on the fly. The type for the values of the transformation object doesn't seem relevant, as long as it's (somehow) compatible with the to be evolved struct.

inArray string filter

Proposal to add a string filter that checks whether a string belongs to an array of records.

Proposed implementation:

export const inArray = <C, B extends A, A extends string = B>(
	a: ReadonlyArray<C>,
	f: (s: string) => (c: C) => boolean
) =>
	S.filter<B, A>((s) => pipe(a, RA.findFirst(f(s)), O.isSome), {
		message: () => `one of the following strings ${a.join(', ')}`
	});

Not equivalent to S.literal insofar as the array of strings to check against is sometimes available only at run-time.

From Discord: Request for Duration.toSeconds in the future

Summary

The user is requesting a new feature called Duration.toSeconds in the future.

Example article

Request for Duration.toSeconds in the future

Introduction

In the Effect-TS ecosystem, there has been a request for a toSeconds function in the Duration module. This function would convert a duration value to seconds. This article will discuss the request and provide a possible implementation for the toSeconds function.

Background

The Effect-TS ecosystem is a collection of libraries that provide various functionalities for building scalable and performant applications. One of the libraries in this ecosystem is the @effect/data library, which includes the Duration module for working with time durations.

Currently, the Duration module provides functions for creating and manipulating duration values, such as fromMilliseconds, fromSeconds, add, and subtract. However, there is no built-in function to convert a duration value to seconds.

Request for toSeconds function

A user in the Effect-TS community has requested a toSeconds function in the Duration module. This function would be useful for scenarios where durations need to be represented in seconds, such as when working with external APIs or performing calculations based on time intervals.

Proposed Implementation

To fulfill the request for a toSeconds function, we can add a new function to the Duration module. Here's a possible implementation:

import { Duration } from '@effect/data';

Duration.prototype.toSeconds = function() {
  return this.toMilliseconds() / 1000;
};

In this implementation, we extend the Duration prototype to add a toSeconds method. This method calculates the duration in milliseconds using the existing toMilliseconds function and then divides it by 1000 to convert it to seconds.

Example Usage

Once the toSeconds function is implemented, it can be used as follows:

import { Duration } from '@effect/data';

const duration = Duration.fromSeconds(60);
const seconds = duration.toSeconds();

console.log(seconds); // Output: 60

In this example, we create a Duration value representing 60 seconds using the fromSeconds function. We then call the toSeconds function on this duration to obtain the duration in seconds.

Conclusion

The request for a toSeconds function in the Duration module of the Effect-TS ecosystem has been addressed with a proposed implementation. This function allows users to convert duration values to seconds, providing flexibility and convenience in various scenarios.

Discord thread

https://discord.com/channels/795981131316985866/1156566778957287484

`Context.merge` too restrictive for tags that have 2 different type args

Steps to reproduce:

interface A {
  readonly _: unique symbol;
}
const A = Context.Tag<A, { foo: string }>();

interface B {
  readonly _: unique symbol;
}
const B = Context.Tag<B, { bar: string }>();

const AplusB = Context.merge(B)(A);

The above results in the following two type errors:

Argument of type 'Tag<A, { foo: string; }>' is not assignable to parameter of type 'Context<unknown>'.
  Type 'Tag<A, { foo: string; }>' is missing the following properties from type 'Context<unknown>': _id, _Sts(2345)

Argument of type 'Tag<B, { bar: string; }>' is not assignable to parameter of type 'Context<unknown>'.
  Type 'Tag<B, { bar: string; }>' is missing the following properties from type 'Context<unknown>': _id, _Sts(2345)

From Discord: Error with @effect/schema in Hono and Cloudflare Worker

Summary

The user is experiencing an error with the @effect/schema library in Hono and Cloudflare Worker. They suspect that the error is due to an outdated TypeScript version or the bundler picking up a tsconfig file from the published package.

Example article

Error with @effect/schema in Hono and Cloudflare Worker

Issue Description

A user reported encountering an error with the @effect/schema library when using it in Hono and Cloudflare Worker. The error seemed to be related to an outdated TypeScript version.

Investigation

The user mentioned that the error message indicated that the library was looking at the TypeScript files. This behavior seemed strange, as the error should have been related to the compiled JavaScript code rather than the TypeScript source files.

The user also suggested that the issue might be related to the presence of a tsconfig file in the published package. It's possible that bundlers are picking up this configuration file and using it during the build process, which could lead to compatibility issues.

Solution

To resolve this issue, we recommend the following steps:

  1. Ensure that you are using a compatible version of TypeScript. Check the documentation of the @effect/schema library to find the recommended TypeScript version.

  2. If you are using a bundler, such as Webpack or Rollup, make sure that it is not picking up any tsconfig files from the published package. Bundlers often have their own configuration files, so it's important to separate the project's configuration from the package's configuration.

  3. If the issue persists, consider reaching out to the maintainers of the @effect/schema library for further assistance. They may be able to provide more specific guidance or help troubleshoot the issue.

Conclusion

The error with @effect/schema in Hono and Cloudflare Worker appears to be related to an outdated TypeScript version or the presence of conflicting tsconfig files. By ensuring the use of a compatible TypeScript version and properly configuring the bundler, the issue should be resolved. If further assistance is needed, contacting the library maintainers is recommended.

Please note that this article provides general guidance and troubleshooting steps. It's always a good idea to consult the official documentation and seek assistance from the library maintainers for specific issues.

Discord thread

https://discord.com/channels/795981131316985866/1148692232078110810

Working with fp-ts data types should be as smooth as possible integrating with the rest of JS

fp-ts types (especially Collections) should generally be representable in JSON and strings, without error or being useless.
Bad cases:

  • chunk.toString() [object Object]
  • JSON.stringify(chunk):
    TypeError: Converting circular structure to JSON
     --> starting at object with constructor 'ChunkImpl'
     --- property 'left' closes the circle
     at JSON.stringify (<anonymous>)
    

Great built-in examples:

  • Array
  • Date

Okay built-in examples:

  • Object (JSON good, string bad)
  • Map (JSON bad {}, string [object Map] - at least mentions Map)
    • this is at least an interesting built-in case, as it ends up as {} in the JSON, I suppose hiding internals might be the least step we should do, when not using another reasonable representation. And having Map in the name instead of Object is also better.

The Chunk problem

Chunk is the most important imo, because it is being used (with good reason) in many places instead of Array, especially in Effect, Config, Cause, errors etc. Leading to problems displaying as string, or dumping to a basic JSON representation.

Of course Encoding and Parsing is not the point of this endeavour, that's what Schema is for. Special rendering is of course left to Show-like's, Printers etc.
But a basic, out of the box useful representation, especially for types that mirror or are extensions/replacements to more basic built-in types like Array, should provide the least friction possible.

My proposal is: Best Effort, basic useful integration with standard JS tools. We all know they have limitations, but that is accepted and out of scope of this proposal.

Example: https://github.com/patroza/data/pull/1/files
My thoughts on string representation are:

  • Chunk(1, 2, 3)
  • Chunk<1, 2, 3>
  • Chunk[1, 2, 3]

When happy/aligned with the proposal, I will proceed with other data types.

Related

Another topic is the way rendering in console.log works, in NodeJS this is done via inspect and there is a symbol you can override. Sadly this is not used in the browser console. And I know for instance @mikearnaldi is of opinion we should not specialise for any particular JS runtimes.
My opinion is slightly milder; as long as it doesn't cause any breakage, then improving the integration in some runtimes is a Nobel cause. However it means the behaviour is different across runtimes.
IMO that is a fact in general, not just limited to fp-ts/effect-ts libraries. I'm not sure that should stop us from at least making it better in some (major) runtimes.

Anyway, to me this is not as important as toString()and toJSON(), which are runtime agnostic.

Documentation Request: Migration guide from fp-ts to effect-ts/data

I am having difficulty understanding the differences between fp-ts and effect-ts/data.

It appears that effect-ts/data is a part of fp-ts without the typeclass.

Why doesn't data in effect-ts/data implement the "typeclass"?

Additionally, how can I convert ReadonlyArray<Option<A>> to Option<ReadonlyArray<A>> if sequence and Traversable are not implemented in effect-ts/data?

Simplify Either data type to represent a simple disjoint union

Currently, there seems to be duplication of functionality between Either and Effect in the context of error handling. This redundancy not only may confuse users when deciding which data type to use but also poses an unnecessary burden on maintainers. To ensure codebase consistency, improve developer experience, and ease maintenance efforts, I propose simplifying the Either data type to represent a simple disjoint union (as used by some type classes).

As a result, certain APIs in Effect that rely on Either, such as runSyncEither and runPromiseEither, can be reevaluated for removal. Instead, we can encourage the use of APIs like runSyncExit and runPromiseExit, which encompass the desired functionality for running effects and handling results.

From Discord: Exploring an 'Automatic' Transformer-less Type Classe 'Derivation'

Summary

In the above conversation, we discussed the idea of an "automatic" transformer-less type class derivation. The main goal was to explore the value and feasibility of addressing the problem of global registries in type class systems.

Here are some key takeaways from the conversation:

  1. The use of global registries in type class systems can lead to issues like lack of tree shakability and potential runtime surprises.
  2. One possible approach to address this is by allowing the creation of app-specific registries, where only the relevant type class instances are registered.
  3. The concept of scopes, as seen in libraries like ArkType, can be a powerful tool for composing custom types and avoiding redundancy.
  4. Scopes provide flexibility in defining relationships between types, handling generics, and enabling user-defined types.
  5. The idea of generating a signature as an intermediate representation and using it to create type class instances can improve developer experience and separate concerns.
  6. It is important to ensure type safety when generating instances by checking if the necessary type class instances are registered.
  7. The use of registries can be optional, allowing developers to choose the level of type safety and flexibility they need.
  8. It would be beneficial to explore existing libraries and approaches, like ArkType, to gain insights and inspiration for solving similar problems.

Overall, the conversation highlighted the need to evaluate the value and trade-offs of introducing an "automatic" transformer-less type class derivation system, considering factors like type safety, performance, and developer experience.

Example article

Exploring an 'Automatic' Transformer-less Type Class 'Derivation'

In this article, we will explore the concept of an 'automatic' transformer-less type class 'derivation' and discuss its potential benefits and drawbacks. We will also take a look at the ArkType library as a reference for similar ideas.

The Problem

When working with type classes in TypeScript, one common challenge is the need for a global registry to associate types with their corresponding instances. This global registry approach can lead to issues with tree shaking and can be cumbersome to manage.

The Proposed Solution

The proposed solution aims to address the challenges of global registries by introducing a more localized approach to type class registration and instance generation. The key idea is to allow users to build an application-specific registry, where they can register the relevant type classes and use them to generate instances.

Advantages

  1. No global registry: By using a localized registry, we eliminate the need for a global registry, which can improve tree shaking and reduce potential conflicts.
  2. Separation of concerns: The proposed solution separates the process of generating a signature (intermediate representation) from creating type class instances. This separation can improve developer experience and make the codebase more modular.
  3. Type safety: The proposed solution ensures type safety by tracking registered type classes at the type level. This helps prevent errors when generating instances from a signature using non-registered type classes.

Limitations

  1. Registry specification: While the proposed solution eliminates the need for a global registry, it still requires users to specify the registry. This can introduce some overhead and may require careful management.
  2. Code generation: Generating instances from a signature may introduce challenges in guaranteeing that the instance can be generated. Careful consideration and validation are required to ensure the generated instances are valid.

Exploring ArkType

ArkType is a library that provides a similar approach to type class registration and instance generation. It uses a global registry to define keywords and allows users to compose custom types using scopes. ArkType has been refined through multiple iterations and offers powerful composition patterns.

Conclusion

The 'automatic' transformer-less type class 'derivation' approach offers an alternative to global registries for managing type classes in TypeScript. While it has its advantages in terms of modularity and type safety, it also comes with some limitations that need to be carefully considered. Libraries like ArkType can serve as a reference for exploring similar ideas and understanding the potential benefits and drawbacks.

Please note that the proposed solution is a proof of concept and may require further refinement and evaluation before being used in production.

Feel free to explore the code examples and further discussions in the chat history above.

(Note: This article is generated based on the chat conversation and may require further editing and formatting.)

Discord thread

https://discord.com/channels/795981131316985866/1142126724570693844

Port Data Structures and TypeClasses from Core

Following shrinkage of core scope the following modules needs porting:

Note: this issue is in WIP
Note: I've ported pretty modules dumbly, i.e. all the new Semigroupal/Monoidal instances should be optimized

Add base64 and base64url and hex encoding & decoding

I believe the effect ecosystem would benefit from built-in base64 and base64url encoding & decoding utils.

Status quo

The relationship status of the web platform and base64 can only be described as "it's complicated".

Most browsers natively provide two JavaScript functions for decoding and encoding Base64 strings(!): btoa (Binary to ASCII) and atob (ASCII to Binary). Notably, these are both not part of the ECMAScript spec,

From https://developer.mozilla.org/en-US/docs/Glossary/Base64

[...] btoa and atob were added to the web platform before it supported binary data types. As a result, the two functions use strings to represent binary data, with the code point of each character representing the value of each byte. This has led to a common misconception that btoa can be used to encode arbitrary text data [...]

However, the byte-to-code-point correspondence only reliably holds true for code points up to 0x7f. Furthermore, code points over 0xff will cause btoa to throw an error due to exceeding the maximum value for 1 byte.

Both, btoa and atob, were also added in Node.js v16 but immediately flagged as deprecated (Stability 3; Legacy). The Node.js documentation continues to recommend the good old Buffer APIs for base64 encoding & decoding.

There is currently a Stage 2 ECMAScript Proposal to add base64 & hex encoding & decoding.

Recommended approach

I suggest we implement a base64, a base64url and a hex module and expose full encoding & decoding implementations without attempting to fall back to native Buffer or atob / btoa APIs. This guarantees cross platform functionality & reproducability until the TC39 base64 proposal is implemented. We can use the Deno implementation as a blueprint which itself is inspired by an implementation by Egor Nepomnyaschih.

I'm going to run some benchmarks to compare performance between atob, btoa, Buffer and the implementations mentioned above.

Reference

Migration plan / roadmap from fp-ts

Is there any roadmap / migration plan from fp-ts? As I understand, you want to merge fp-ts and effect-ts (great!). I would be interested if the These module from fp-ts will make it into this library?

Not working anymore with react-native since #486

After #486 got merged I now need a polyfill for TextEncoder for react-native. Just wanted to point out that TextEncoder is not (yet) in the ECMAScript spec:
facebook/hermes#948 (comment)

Just wanted to point this out, you may close this if this is not considered an issue but I'm not sure if it's a good idea for this core package to use non ECMAScript Spec APIs.

`Unify` returns unexpected type for records

Unify<{ [x: string]: string }>

results in

string | number | ((index: number) => string | undefined) | {
    [x: string]: string;
} | IterableIterator<string> | ((searchString: string, position?: number | undefined) => number) | ((searchString: string, position?: number | undefined) => number) | ((searchString: string, position?: number | undefined) => boolean) | ((pos: number) => string) | ((index: number) => number) | {
    (that: string): number;
    (that: string, locales?: string | string[] | undefined, options?: Intl.CollatorOptions | undefined): number;
} | {
    (regexp: string | RegExp): RegExpMatchArray | null;
    (matcher: {
        [Symbol.match](string: string): RegExpMatchArray | null;
    }): RegExpMatchArray | null;
} | {
    (searchValue: string | RegExp, replaceValue: string): string;
    (searchValue: string | RegExp, replacer: (substring: string, ...args: any[]) => string): string;
    (searchValue: {
        [Symbol.replace](string: string, replaceValue: string): string;
    }, replaceValue: string): string;
    (searchValue: {
        [Symbol.replace](string: string, replacer: (substring: string, ...args: any[]) => string): string;
    }, replacer: (substring: string, ...args: any[]) => string): string;
} | {
    (regexp: string | RegExp): number;
    (searcher: {
        [Symbol.search](string: string): number;
    }): number;
} | {
    (separator: string | RegExp, limit?: number | undefined): string[];
    (splitter: {
        [Symbol.split](string: string, limit?: number | undefined): string[];
    }, limit?: number | undefined): string[];
} | ((start: number, end?: number | undefined) => string ...

Tried on TS 5.0.1, 5.1.3 and next (5.2.0-dev.20230611)

repro

RedBlackTree lessThan/lessThanEqual behavior

πŸ› Bug report

Current Behavior

Apologies if this is working as intended, but there aren’t currently any tests for lessThan/lessThanEqual, and the behavior seems unintuitive.

Given a RedBlackTree like

const tree = pipe(
  RedBlackTree.empty<number, string>(number.Order),
  RedBlackTree.insert(1, "a"),
  RedBlackTree.insert(0, "b"),
  RedBlackTree.insert(-1, "c"),
  RedBlackTree.insert(-2, "d"),
  RedBlackTree.insert(3, "e")
)

lessThanEqual produces the same result as greaterThanEqual

deepStrictEqual(
  Array.from(RedBlackTree.lessThanEqual(0.5)(tree)),
  Array.from(RedBlackTree.greaterThanEqual(0.5)(tree))
)

Expected behavior

I would expect lessThanEqual to produce a different result than greaterThanEqual. My expectation has been corrected here; they will produce the same result if passed a key that appears in the RedBlackTree, but they should not produce the same result otherwise.

Reproducible example

Suggested solution(s)

I'm not entirely sure of the intended behavior, but currently lessThanEqual has the same implementation as greaterThanEqual, I'm guessing the intent was to reverse the Direction of the former? I have a PR here to modify the implementation to return an iterator from the greatest element that is less than or equal to the passed in key: #156

Additional context

Your environment

Latest main branch.

Software Version(s)
@fp-ts/data 0.0.27
TypeScript 4.9.4

inconsistent typing for tuples (or nonempty arrays)

some combinators take or return a mutable tuple: String.split returns [string, ...string[]]
some combinators take or return a readonly tuple: ReadonlyArray.lastNonEmpty takes readonly [A, ...A[]]

I understand how this drift happened, but it's uncomfortable to work with.

Traits for the greater good

We currently have the following traits:

  • Hash : to represents objects that can be hashed
  • Equal : to represent equality between objects

We can see traits as typeclasses that are directly implemented in the data rather than presented as separated instances, they have the advantage of not requiring to pass explicitly instances and can make DX better.

There are other scenarios that came up as useful, those are:

  • Compare : like a partial order that enables implementation of things like sortBy(array, p => [p.age, p.name]) without manually constructing an Order instance (that one should be able to still do)
  • *Copy : encodes the ability to copy an object to a separated instance
  • *Index : enables indexed access and updates over a structure that is indexed (e.g. Chunk, HashMap, etc)

Note: The ones marked with * are coming from requirements in optics.

Specification for the `toJSON` and `toString` Methods of Data Types

toJSON Method:

The toJSON method should provide a JSON representation of the data type. It should include the following fields:

  • An _id field that contains the name of the module containing the data type.
  • A series of fields depending on the data type's characteristics. If the data type is a tagged union (public), it should include the _tag field.

Example:

{
  "_id": "ModuleName",
  "field1": "value1",
  "field2": "value2"
}

The toString method is a simplified representation similar to YAML of what is returned by the toJSON method. It aims to provide a human-readable version of the data type.

It aims to provide a human-readable version of the data type, it can be obtained using the JSON.stringify(this.toJSON(), null, 2)

FreeSemigroup

At the moment we are using ReadonlyArray as the "idiomatic free semigroup" but while mathematically all the possible representations of a free semigroup are equivalent the same does not stand true for programming, in fact the free semigroup based on ReadonlyArray is the worst possible structure to use for repeated "concat".

A better structure for a free semigroup would have O(1) concat, for example:

type FreeSemi<A> = Value<A> | Concat<A>

or even better would be using Chunk that has as O(1):

  • append / prepend
  • concat
  • slice / take / drop

Missing functions in String module

Just as a reminder:

  • charCodeAt
  • substring
  • at
  • charAt
  • codePointAt
  • indexOf
  • lastIndexOf
  • localeCompare
  • match
  • matchAll
  • normalize
  • padEnd
  • padStart
  • repeat
  • replaceAll
  • search
  • toLocaleLowerCase
  • toLocaleUpperCase

Port Data Structures and Modules from Effect

To be ported from @tsplus/stdlib:

No duplicates array filter

Proposal to add a noDups array filter.

Suggested implementation:

export const noDups = <C>(isEquivalent: (self: C, that: C) => boolean) =>
	S.filter<ReadonlyArray<C>>(
		(a) => pipe(a, RA.uniq(isEquivalent), (a1) => a1.length === a.length),
		{
			message: (a) => `${JSON.stringify(a)} should not contain duplicates`
		}
	);

This filter is useful to check whether a sub-array of array columns forma key

Fix examples in every module

When porting we changed the module structure and some functions behave differently (like uniq non requiring Eq<A>) this made all examples no longer compiling, we should go through each module and fix the examples and add examples where missing.

The process should be:
1: change the jsdoc tag @exampleTodo to @example
2: fix the example code
3: run pnpm run docs and make sure it is compiling

Modules:

  • Boolean
  • Chunk
  • Const
  • Context
  • Differ & Patches
  • Duration
  • Endomorphism
  • Equal
  • Function
  • HashMap
  • HashSet
  • Identity
  • List
  • MutableHashMap
  • MutableHashSet
  • MutableList
  • MutableListBuilder
  • MutableQueue
  • MutableRef
  • NonEmptyReadonlyArray
  • Number
  • Option
  • Predicate
  • Queue
  • ReadonlyArray
  • RedBlackTree
  • Refinement
  • Result
  • Either
  • SafeEval
  • SortedMap
  • SortedSet
  • String
  • Compactable
  • Filterable
  • TraversableFilterable
  • WeakIterableMap

From Discord: webpack is not mangling @effect/data/mjs/* properly

Summary

The user is reporting an issue with webpack not properly mangling the code of the @effect/data library. They mention that they have encountered this issue before and will try to work around it. Another user expresses confusion about why webpack is skipping the assignment of a symbol. The conversation continues with speculation about the cause of the issue and attempts to reproduce it. However, the user is unable to reproduce the problem with a minimal example. The assistant acknowledges the user's efforts and thanks them for providing valuable information.

Key takeaways:

  • There is an issue with webpack not mangling the code of the @effect/data library properly.
  • The cause of the issue is unclear and attempts to reproduce it have been unsuccessful so far.
  • The user is planning to adapt the library code to work around the problem.

Example article

Webpack is not mangling @effect/data/mjs/* properly

Introduction

In the Effect-TS ecosystem, there have been reports of Webpack not properly mangling the @effect/data/mjs/* module. This issue can cause problems with symbol assignment and can lead to unexpected behavior. In this article, we will explore the issue and discuss potential workarounds.

The Problem

The issue arises when Webpack attempts to assign symbols to the @effect/data/mjs/* module. Instead of assigning the symbol directly or using a conventional assignment, Webpack seems to skip the assignment step altogether. This behavior can result in incorrect symbol references and cause the module to malfunction.

Reproducing the Problem

To reproduce the problem, follow these steps:

  1. Set up a project with the Effect-TS ecosystem and Webpack.
  2. Import the @effect/data/mjs/* module in your code.
  3. Build the project using Webpack.

You may encounter issues with symbol assignment and unexpected behavior during runtime.

Investigation

Upon further investigation, it appears that the problem is not easily reproducible with a minimal example. This suggests that there might be additional factors at play. It is possible that the issue is related to specific configurations or dependencies within the project.

Workarounds

While a direct solution to the Webpack mangling issue is not available at the moment, there are a few workarounds that can be employed:

1. Readapting Library Code

One possible workaround is to modify the library code to work around the Webpack mangling issue. By making adjustments to the code, you can ensure that the symbols are assigned correctly and that the module functions as expected.

2. Investigate Configuration and Dependencies

Since the issue might be related to specific configurations or dependencies, it is worth investigating these aspects of your project. Check if there are any conflicting settings or dependencies that could be causing the problem. Updating or adjusting these configurations might help resolve the issue.

3. File a Bug Report

If you are unable to find a suitable workaround or determine the root cause of the issue, consider filing a bug report with the Webpack or Effect-TS community. Providing a detailed description of the problem, along with any relevant code examples or error messages, can help the community identify and address the issue more effectively.

Conclusion

The Webpack mangling issue with the @effect/data/mjs/* module in the Effect-TS ecosystem can lead to symbol assignment problems and unexpected behavior. While a direct solution is not available, there are workarounds such as readapting library code, investigating configurations and dependencies, and filing bug reports. By employing these strategies, you can mitigate the impact of the issue and continue working with the Effect-TS ecosystem effectively.

Discord thread

https://discord.com/channels/795981131316985866/1130445205225615462

From Discord: Finding an Entry in a HashMap by Person's Name

Summary

The user is concerned about the memory usage when searching for an entry in a HashMap by a person's name using the Chunk data structure. They suggest using a findFirst method on a Chunk created from the HashMap. However, it is noted that this approach may be wasteful in terms of memory usage.

The key takeaway is that there is a need for a more efficient way to find an entry in a HashMap by a person's name without unnecessary memory overhead.

Example article

Finding an Entry in a HashMap by Person's Name

When working with a HashMap, there may be times when you need to find an entry based on a specific condition. In this case, we want to find an entry in a HashMap by a person's name. Let's explore how we can achieve this using the Effect-TS ecosystem.

Using Chunk to Find an Entry

One approach to finding an entry in a HashMap is by converting it to a Chunk and then using the findFirst method. Here's an example:

import { Chunk } from "@effect/data";
import { HashMap } from "@effect/data/HashMap";

const map = new HashMap<string, Person>();

// Populate the HashMap with data
map.set("1", { name: "Alice", age: 25 });
map.set("2", { name: "Bob", age: 30 });
map.set("3", { name: "Jules", age: 28 });

const entry = pipe(
  Chunk.fromIterable(map),
  Chunk.findFirst(([k, v]) => v.name === "Jules")
);

console.log(entry); // [ '3', { name: 'Jules', age: 28 } ]

In the above example, we create a HashMap called map and populate it with some data. We then convert the map to a Chunk using Chunk.fromIterable. Finally, we use the findFirst method to find the first entry where the person's name is "Jules".

While this approach works, it may not be the most memory-efficient solution, as it requires converting the entire HashMap to a Chunk.

Adding a find Method

To make the process more efficient, we can create a custom method called find that directly searches for an entry in the HashMap based on the person's name. Here's an example implementation:

import { HashMap } from "@effect/data/HashMap";

HashMap.prototype.find = function (name: string): [string, Person] | undefined {
  for (const [key, value] of this) {
    if (value.name === name) {
      return [key, value];
    }
  }
  return undefined;
};

const map = new HashMap<string, Person>();

// Populate the HashMap with data
map.set("1", { name: "Alice", age: 25 });
map.set("2", { name: "Bob", age: 30 });
map.set("3", { name: "Jules", age: 28 });

const entry = map.find("Jules");

console.log(entry); // [ '3', { name: 'Jules', age: 28 } ]

In this example, we extend the HashMap prototype to add a find method. This method iterates over the HashMap and returns the first entry where the person's name matches the provided name. If no entry is found, it returns undefined.

By using the find method, we can directly search for the desired entry without the need to convert the entire HashMap to a Chunk.

Conclusion

Finding an entry in a HashMap by a person's name can be achieved using the Effect-TS ecosystem. While using Chunk and findFirst is one approach, it may not be the most memory-efficient solution. Alternatively, you can create a custom find method to directly search for the entry based on the person's name. Choose the approach that best suits your specific use case.

Discord thread

https://discord.com/channels/795981131316985866/1137716253214130258

Expose Iterable publicly

Some of the existing operators return Iterable and some others also expect Iterable.
However, due to lack of public operators (like map) on Iterable, we now have to leverage ReadonlyArray in the middle, which can be omitted if internal/Iterable is made public.

declare const set1: HashSet<string>;
declare const set2: HashSet<string>;

const tagged: HashMap<"tag1" | "tag2", string> = pipe(
  HS.values(set1),
  RA.fromIterable,
  RA.map(v => ["tag1", v]),
  RA.appendAll(pipe(
      HS.values(set2),
      RA.fromIterable,
      RA.map(v => ["tag2", v])
  )),
  HM.fromIterable
);

// becomes
const tagged: HashMap<"tag1" | "tag2", string> = pipe(
  HS.values(set1),
  Iterable.map(v => ["tag1", v]),
  Iterable.concat(pipe(
    HS.values(set2),
    Iterable.map(v => ["tag2", v])
  )),
  HM.fromIterable
)

Pending TODOs

The following PRs should be taken as a template to port across the remaining modules, namely:

  • #90 brings 100% test coverage to the List module and implements typeclass instances from @fp-ts/core remaining work is to do the same for all collections

  • #91 sets a standard on function naming for all array-like modules remaining work is to port it to the remaining collections

Minimal set of methods in every module

Currently every module is purely pipeable, while this is the theoretical best for tree-shaking it leads to forcing pipe, and explicit imports, everywhere.

Let's see a few examples of that:

import * as Option from "@fp-ts/data/Option"

const logSome = <A>(o: O.Option<A>) => {
  if (O.isSome(o)) {
    console.log(o.value)
  }
}

const increment = <A>(o: O.Option<A>) => pipe(o, O.map(n => n + 1))

This leaves the user in a place where native types are pretty much always preferred, for example Array over Chunk, Nullable over Option, Map over HashMap, and so on and so forth.

When inspecting source code there are a number of functions that will end up in the bundle regardless, for example functions like isSome / isNone / map for Option, for such functions we are really not gaining anything by not putting them as methods too.

There may be an argument that methods cannot be minified but they can still be compressed, i.e. when using something like gzip that all the servers should support by default so the increase in minimum bundle size will be only apparent in uncompressed form.

Adding methods can also enable the addition of a pipe method that will provide the ability to do: a.pipe(O.zip(b), O.zipFlatten(c)).

Even adding a very minimum set of methods would smooth usage in the majority of the cases and would provide an API which is optimized for dot-completion.

Finally the only argument is serialization but:

  1. it is domain for /schema
  2. nobody who uses Option actually serializes it and deserializes it via JSON.parse/stringify, if they use direct JSON they will use nullable fields

This proposal follows the same reasoning behind method addition in ecosystem libraries such as /optic, we shouldn't make it more complex to use @fp-ts/data for no real benefit.

Note: Currently we have improper subtyping relationships, namely we have Either as a subtype of These, in languages like Haskell where those types originate every ADT is fully independent. While generally I don't like to draw too many parallelisms between languages I do think it would be a good idea not to force those artificial type-level relationships but opt in for explicit conversions (like these.toEither())

From Discord: Missing Alternative Instance for Either

Summary

The chat is about a missing alternative instance for the Either type in the Effect-TS ecosystem. The user is pointing out that there is no Alternative instance defined for Either in the library.

Some key takeaways from the chat are:

  • The user is looking for an Alternative instance for Either in the Effect-TS ecosystem.
  • The Alternative type class represents types that have a choice between two alternatives.
  • The absence of an Alternative instance for Either means that certain operations and combinators defined for Alternative cannot be used directly with Either in the Effect-TS ecosystem.

Example article

Missing Alternative Instance for Either

In the Effect-TS ecosystem, the Either type is a common choice for representing computations that may result in either a successful value or an error. However, it seems that there is a missing Alternative instance for the Either type.

The Alternative typeclass provides a way to combine computations that have a choice between multiple alternatives. It includes the empty operation, which represents a computation that has no successful value, and the orElse operation, which allows choosing between two computations.

Let's take a look at an example to understand how the Alternative typeclass works. Suppose we have two computations that may result in an Either value:

import { Either, left, right } from '@effect/data/either'

const computation1: Either<string, number> = left('Error 1')
const computation2: Either<string, number> = right(42)

If we want to combine these computations using the Alternative typeclass, we would expect to be able to write code like this:

import { Alternative } from '@effect/data/alternative'

const combinedComputation: Either<string, number> = Alternative.orElse(computation1, computation2)

However, currently, the Alternative typeclass does not provide an instance for the Either type. This means that we cannot use the orElse operation directly on Either values.

To work around this limitation, we can define our own Alternative instance for the Either type. Here's an example implementation:

import { Alternative, Alternative1 } from '@effect/data/alternative'
import { Either, left, right } from '@effect/data/either'

const eitherAlternative: Alternative1<Either<string>> = {
  ...Alternative,
  orElse: (fa, fb) => {
    return fa.fold(
      () => fb,
      () => fa
    )
  }
}

In this implementation, we define a new eitherAlternative object that extends the existing Alternative instance. We override the orElse operation to handle Either values by folding over them. If the first computation (fa) is a Left, we return the second computation (fb). Otherwise, we return the first computation (fa).

With this custom Alternative instance, we can now use the orElse operation on Either values:

const combinedComputation: Either<string, number> = eitherAlternative.orElse(computation1, computation2)

This allows us to combine computations that may result in an Either value using the Alternative typeclass.

It's worth noting that this missing Alternative instance for Either has been identified as an issue in the Effect-TS ecosystem. The maintainers are actively working on adding this instance in a future release. In the meantime, you can use the workaround described above to define your own Alternative instance for Either.

In conclusion, while there is currently a missing Alternative instance for the Either type in the Effect-TS ecosystem, you can define your own instance to work around this limitation. The example implementation provided in this article demonstrates how to define a custom Alternative instance for Either and use it to combine computations that may result in an Either value.

Discord thread

https://discord.com/channels/795981131316985866/1154676286204162149

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.