Repository: gcanti/fp-ts (ts)

Issue:
ReadonlyRecord.Traverse* misbehaves
## 🐛 Bug report

### Current Behavior

I've been using traverse on a record using array as an Applicative and the result is not as expected.
The values are all the same and only are the last ones. Looks like it is overwriting all the records every time.

<!-- If applicable, add screenshots to help explain your problem. -->

### Expected behavior

```sh
    Expected value to deeply and strictly equal to:
      [{"a": 0, "b": 0}, {"a": 0, "b": 4}, {"a": 0, "b": 8}, {"a": 4, "b": 0}, {"a": 4, "b": 4}, {"a": 4, "b": 8}]
    Received:
      [{"a": 4, "b": 8}, {"a": 4, "b": 8}, {"a": 4, "b": 8}, {"a": 4, "b": 8}, {"a": 4, "b": 8}, {"a": 4, "b": 8}]
```

<!-- A clear and concise description of what you expected to happen. -->

### Reproducible example

https://github.com/gcanti/fp-ts/compare/master...waynevanson:bug.traverse-record

https://github.com/waynevanson/fp-ts/tree/bug.traverse-record

### Suggested solution(s)

No idea, I tried but failed.

<!-- How could we solve this bug? What changes would need to made to fp-ts? -->

### Additional context

<!-- Add any other context about the problem here.  -->

Permutations.

### Your environment

Current version.

<!-- PLEASE FILL THIS OUT -->
This was ran inside the repo.

| Software   | Version(s) |
| ---------- | ---------- |
| fp-ts      |       fp-ts     |
| TypeScript |         fp-ts   |


Pull request description:
fix(ReadonlyRecord): `traverse` clones objects in place instead of referencing and assigning to them.
closes #1703 

Patch:
diff --git a/src/ReadonlyRecord.ts b/src/ReadonlyRecord.ts
index 5e971c2a..f7a02964 100644
--- a/src/ReadonlyRecord.ts
+++ b/src/ReadonlyRecord.ts
@@ -1430,10 +1430,7 @@ const _traverseWithIndex = (O: Ord<string>) => <F>(
     let fr: HKT<F, Record<string, B>> = F.of({})
     for (const key of ks) {
       fr = F.ap(
-        F.map(fr, (r) => (b: B) => {
-          r[key] = b
-          return r
-        }),
+        F.map(fr, (r) => (b: B) => Object.assign({}, r, { [key]: b })),
         f(key, ta[key])
       )
     }


Test patch:
diff --git a/test/ReadonlyRecord.ts b/test/ReadonlyRecord.ts
index 44f8a246..9d5cebc4 100644
--- a/test/ReadonlyRecord.ts
+++ b/test/ReadonlyRecord.ts
@@ -289,11 +289,30 @@ describe('ReadonlyRecord', () => {
       )
     })
 
-    it('traverseWithIndex', () => {
-      const f = (k: string, n: number): O.Option<number> => (k !== 'a' ? O.some(n) : O.none)
-      const traverseWithIndex = _.traverseWithIndex(O.Applicative)(f)
-      U.deepStrictEqual(pipe({ a: 1, b: 2 }, traverseWithIndex), O.none)
-      U.deepStrictEqual(pipe({ b: 2 }, traverseWithIndex), O.some({ b: 2 }))
+    describe('traverseWithIndex', () => {
+      it('simple Traversal', () => {
+        const f = (k: string, n: number): O.Option<number> => (k !== 'a' ? O.some(n) : O.none)
+        const traverseWithIndex = _.traverseWithIndex(O.Applicative)(f)
+        U.deepStrictEqual(pipe({ a: 1, b: 2 }, traverseWithIndex), O.none)
+        U.deepStrictEqual(pipe({ b: 2 }, traverseWithIndex), O.some({ b: 2 }))
+      })
+
+      it('should not modify arrays in place', () => {
+        const result = pipe(
+          { a: 2, b: 3 },
+          _.fromRecord,
+          _.traverseWithIndex(RA.Applicative)((_, n) => RA.makeBy(n, (i) => i * 4))
+        )
+
+        U.deepStrictEqual(result, [
+          { a: 0, b: 0 },
+          { a: 0, b: 4 },
+          { a: 0, b: 8 },
+          { a: 4, b: 0 },
+          { a: 4, b: 4 },
+          { a: 4, b: 8 }
+        ])
+      })
     })
 
     it('getTraversableWithIndex', () => {