Does a legitimate array monad transformer exist?

I have successfully implemented the single linked list monad transformer, however, I am struggling to get its array counterpart functioning properly. The issue stems from a grouping effect that restricts the transformer's validity to commutative base monads only. To illustrate this problem, both the transformer and base monad are arrays in the following example without any type wrapper:

// ARRAY

const arrMap = f => xs =>
  xs.map((x, i) => f(x, i));

const arrAp = tf => xs =>
  arrFold(acc => f =>
    arrAppend(acc)
      (arrMap(x => f(x)) (xs)))
        ([])
          (tf);

const arrOf = x => [x];

const arrChain = mx => fm =>
  arrFold(acc => x =>
    arrAppend(acc) (fm(x))) ([]) (mx);

// Transformer

const arrChainT = ({map, ap, of ,chain}) => mmx => fmm =>
  chain(mmx) (mx => {
    const go = ([x, ...xs]) =>
      x === undefined
        ? of([])
        : ap(map(arrCons) (fmm(x))) (go(xs));

    return chain(go(mx)) (ys => of(arrFold(arrAppend) ([]) (ys)));
  });

const arrOfT = of => x => of([x]);

// Transformer stack

const arrArrChain = arrChainT(
  {map: arrMap, ap: arrAp, of: arrOf, chain: arrChain});

const arrArrOf = arrOfT(arrOf);

// auxiliary functions

const arrFold = f => init => xs => {
  let acc = init;
  
  for (let i = 0; i < xs.length; i++)
    acc = f(acc) (xs[i], i);

  return acc;
};

const arrAppend = xs => ys =>
  xs.concat(ys);

const arrCons = x => xs =>
  [x].concat(xs);

// MAIN

foo = x =>
  x === 0
    ? [[0, 1]]
    : [[0], [1]];

console.log(JSON.stringify(
  arrArrChain(arrArrChain(foo(0)) (foo)) (foo)));
    // yields [[0,1,0,0,1],[0,1,1,0,1],[0,1,0,0],[0,1,0,1],[0,1,1,0],[0,1,1,1]]

console.log(JSON.stringify(
  arrArrChain(foo(0)) (x => arrArrChain(foo(x)) (foo))));
    // yields [[0,1,0,0,1],[0,1,0,0],[0,1,0,1],[0,1,1,0,1],[0,1,1,0],[0,1,1,1]]

Both scenarios should produce the same outcome. Hence, my question stands: Is there a way to implement the array transformer in a lawful manner?

Answer №1

The array monad transformer is similar to the list monad transformer.

// Step m a = null | { head : a, tail : ListT m a }
// ListT m a = m (Step m a)

// nil : Monad m -> ListT m a
const nil = M => M.pure(null);

// cons : Monad m -> a -> ListT m a -> ListT m a
const cons = M => head => tail => M.pure({ head, tail });

// foldr : Monad m -> (a -> m b -> m b) -> m b -> ListT m a -> m b
const foldr = M => f => a => m => M.bind(m)(step =>
    step ? f(step.head)(foldr(M)(f)(a)(step.tail)) : a);

// append : Monad m -> ListT m a -> ListT m a -> ListT m a
const append = M => m1 => m2 => foldr(M)(cons(M))(m2)(m1);

// pure : Monad m -> a -> ListT m a
const pure = M => x => cons(M)(x)(nil(M));

// bind : Monad m -> ListT m a -> (a -> ListT m b) -> ListT m b
const bind = M => m => f => foldr(M)(x => append(M)(f(x)))(nil(M))(m);

// MonadListT : Monad m -> Monad (ListT m)
const MonadListT = M => ({ pure: pure(M), bind: bind(M) });

// MonadArray : Monad Array
const MonadArray = { pure: x => [x], bind: a => f => a.flatMap(f) };

// MonadListArray : Monad (ListT Array)
const MonadListArray = MonadListT(MonadArray);

// fromArray : Monad m -> Array a -> ListT m a
const fromArray = M => a => a.reduceRight((xs, x) => cons(M)(x)(xs), nil(M));

// lift : Monad m -> m a -> ListT m a
const lift = M => m => M.bind(m)(pure(M));

// foo : Nat -> ListT Array Nat
const foo = x => x === 0
    ? fromArray(MonadArray)([0, 1])
    : lift(MonadArray)([0, 1]);

// associativityLHS : Monad m -> m a -> (a -> m b) -> (b -> m c) -> m c
const associativityLHS = M => m => k => h => M.bind(M.bind(m)(k))(h);

// associativityRHS : Monad m -> m a -> (a -> m b) -> (b -> m c) -> m c
const associativityRHS = M => m => k => h => M.bind(m)(x => M.bind(k(x))(h));

// lhs :: ListT Array Nat
const lhs = associativityLHS(MonadListArray)(foo(0))(foo)(foo);

// rhs :: ListT Array Nat
const rhs = associativityRHS(MonadListArray)(foo(0))(foo)(foo);

console.log(JSON.stringify(lhs) === JSON.stringify(rhs));
console.log(JSON.stringify(lhs));

It should be noted that each step of the list is enclosed in the argument monad. This allows for the integration of other monadic actions, and it is important to uphold the monad laws if the argument monad is non-commutative.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

JavaScript Callbacks and Promises: Exploring Asynchronous Programming

For the past 2 days, I've been struggling to figure out my mistake. It seems that I can't get past the "undefined" returned value because I'm having trouble grasping callbacks or promises. Despite reading numerous posts and trying simple exa ...

What is the method for determining the global/world position of a child object?

Is there a way to retrieve the global position of an Object3D nested inside another Object3D? scenario: var parent = new THREE.Object3D(); parent.position.set(100, 100, 100); var child = new THREE.Object3D(); child.position.set(100, 100, 100); parent.ad ...

Comparison between the sluggishness of radio buttons in Internet Explorer 10 versus Chrome when using jQuery

When using IE 10, Chrome, and jquery 1.10.2 with the standard template from Visual Studio 2013 (.Net 4.5), I encounter an issue with radio buttons. Upon clicking a radio button, two actions are supposed to occur: 1. Recreation of all the radio buttons. 2 ...

How to install Typed.js without using Node.js

I'm currently working on a project that requires the JavaScript library typed.js, however, I am using the free webhost 000webhost which does not support Node.js. Since typed.js is typically installed using Yarn, NPM, or Bower - all of which require No ...

Unable to retrieve or remove cookie sent from Express on client side

My Express server is sending a cookie to the client that is not httpOnly. Despite this, the client is unable to access the cookie through document.cookie or see it in the Application tab on chrome dev tools. Interestingly, I am able to view the cookie in C ...

Issue encountered: "An error has occurred stating that your cache folder contains files owned by root. This is likely a result of a bug present in older versions of npm. This issue arose during the execution of the

While attempting to create a new react app using the command npx create-react-app example_app, I encountered the following issue: [ Your cache folder contains root-owned files, due to a bug in previous versions of npm which has since been addressed sudo ...

Error with SwitchMap on ActivatedRoute.paramMap

When I try to run the ngOnInit method of my component, I encountered an error with the following line of code. this.products$ = this.route.paramMap.switchMap((params: ParamMap) => this.getProductsForType(params.get('type'))); The error mes ...

Having trouble with Google font displaying on React site but not on mobile devices?

After importing a Google font that appears perfectly on desktop and cross-browser, an issue arises on mobile where default fonts are shown instead. Below is a snippet from my App.css file: @import url("https://fonts.googleapis.com/css2?family=Turret+Ro ...

Utilizing a Web Interface for Remote Monitoring of Windows Servers

I am in need of creating a webpage that will indicate whether a server is currently operational or not. These servers are all Windows based, with some running on 2008 and others on 2003. They are spread across different networks within various client locat ...

Uploading and previewing multiple files, including images and videos

I am facing an issue with a child component where the input field is obscured by a slotted element. The parent component provides the slotted click event element, along with a preview of the file(s) and the option to delete them. I am struggling to impleme ...

Utilizing MoTools Ajax for form submission and implementing MoTools Form Check for form validation

I am utilizing a motools script for client side validation of my web form. If you're interested, you can find the link to that script here. My issue arises when I attempt to use both form validation and submit the form using AJAX with motools. It app ...

Can we retrieve props that have not been explicitly passed down?

How can I access the prop "showPopover" from the constructor or another method? This prop was originally created in a separate component and now that I've integrated it into this component, I'm looking for a way to easily retrieve and modify it. ...

Attempting to have this .js lightbox appear as soon as the page loads

This Lightbox is absolutely stunning! However, I am looking for a way to automatically trigger the lightbox when the page loads. ...

Input a new function

Trying to properly type this incoming function prop in a React Hook Component. Currently, I have just used any which is not ideal as I am still learning TypeScript: const FeaturedCompanies = (findFeaturedCompanies: any) => { ... } This is the plain fun ...

Generating a JavaScript array using concealed data

var a1=$("#orderprogress").val().toFixed(2);//a1=50 var a2=$("#poprogress").val().toFixed(2); //a2=70 If I were to create an array in this format, how should I proceed? graphData = new Array( [a1 value,'#222222'],//[50,'#22222 ...

What could be causing my onclick code to activate on double click the very first time?

Currently, I am developing a straightforward popup for my Nextjs website. The implementation involves using the onclick attribute to toggle the display property in CSS between none and block based on the ID of the element. Although the functionality works ...

Refreshing Angular 7 application does not function properly on IIS despite having the web.config properly set up

Something strange is happening with my Angular 7 application. Whenever I click refresh, the routes seem to be lost and a 404 error is displayed. Despite following advice to configure the web.config file, this behavior persists. Here's a snippet from ...

What could be causing spacing problems with fontawesome stars in Vue/Nuxt applications?

Currently, I am working on implementing a star rating system in Nuxt.js using fontawesome icons. However, I have encountered an issue where there is a strange whitespace separating two different stars when they are placed next to each other. For instance, ...

Intermittent loading issues with Highcharts using AJAX requests

We've encountered intermittent errors when using Highcharts. Despite our efforts, we can't seem to pinpoint the cause of these issues in Chrome: Uncaught Highcharts error #16: www.highcharts.com/errors/16 VM210:16 HighCharts was already loaded V ...

Trigger greensock (JS) animation reset upon reaching a window width of 865px

Currently, I am utilizing the js greensock animation library to create a unique animation. The animation pauses when the window is being resized and resumes once the resizing has stopped. Now, my goal is to enable the animation to either restart or begin ...