Skip to content

Commit cbc3a0f

Browse files
committed
feat(semigroup): add partial semigroup
1 parent 6d6f7d8 commit cbc3a0f

3 files changed

Lines changed: 83 additions & 0 deletions

File tree

docs/modules/Semigroup.ts.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Added in v2.0.0
5050

5151
- [combinators](#combinators)
5252
- [intercalate](#intercalate)
53+
- [partial](#partial)
5354
- [reverse](#reverse)
5455
- [struct](#struct)
5556
- [tuple](#tuple)
@@ -110,6 +111,39 @@ assert.strictEqual(S1.concat('a', 'b'), 'a + b')
110111

111112
Added in v2.10.0
112113

114+
## partial
115+
116+
Given a struct of semigroups returns a semigroup for the struct, that can have optional keys.
117+
118+
**Signature**
119+
120+
```ts
121+
export declare const partial: <A>(
122+
semigroups: { [K in keyof A]: Semigroup<A[K]> }
123+
) => Semigroup<{ readonly [K in keyof Partial<A>]: A[K] }>
124+
```
125+
126+
**Example**
127+
128+
```ts
129+
import { partial, last } from 'fp-ts/Semigroup'
130+
import * as S from 'fp-ts/string'
131+
132+
interface Person {
133+
readonly name: string
134+
readonly age: number
135+
}
136+
137+
const S1 = partial<Person>({
138+
name: S.Semigroup,
139+
age: last(),
140+
})
141+
142+
assert.deepStrictEqual(S1.concat({ name: 'first', age: 42 }, { name: 'second' }), { name: 'firstsecond', age: 42 })
143+
```
144+
145+
Added in v2.10.0
146+
113147
## reverse
114148

115149
The dual of a `Semigroup`, obtained by swapping the arguments of `concat`.

src/Semigroup.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,46 @@ export const struct = <A>(
158158
}
159159
})
160160

161+
/**
162+
* Given a struct of semigroups returns a semigroup for the struct, that can have optional keys.
163+
*
164+
* @example
165+
* import { partial, last } from 'fp-ts/Semigroup'
166+
* import * as S from 'fp-ts/string'
167+
*
168+
* interface Person {
169+
* readonly name: string
170+
* readonly age: number
171+
* }
172+
*
173+
* const S1 = partial<Person>({
174+
* name: S.Semigroup,
175+
* age: last()
176+
* })
177+
*
178+
* assert.deepStrictEqual(S1.concat({ name: "first", age: 42 }, { name: "second" }), { name: "firstsecond", age: 42 })
179+
*
180+
* @category combinators
181+
* @since 2.10.0
182+
*/
183+
export const partial = <A>(
184+
semigroups: { [K in keyof A]: Semigroup<A[K]> }
185+
): Semigroup<{ readonly [K in keyof Partial<A>]: A[K] }> => ({
186+
concat: (first, second) => {
187+
const r: A = {} as any
188+
for (const k in semigroups) {
189+
if (_.has.call(semigroups, k)) {
190+
if (_.has.call(first, k) && _.has.call(second, k)) {
191+
r[k] = semigroups[k].concat(first[k], second[k])
192+
} else if (_.has.call(first, k) || _.has.call(second, k)) {
193+
r[k] = second[k] || first[k]
194+
}
195+
}
196+
}
197+
return r
198+
}
199+
})
200+
161201
/**
162202
* Given a tuple of semigroups returns a semigroup for the tuple.
163203
*

test/Semigroup.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ describe('Semigroup', () => {
5757
U.deepStrictEqual(S.concat({}, {}), {})
5858
})
5959

60+
it('partial', () => {
61+
// should ignore non own properties
62+
const S1 = _.partial(Object.create({ a: 1 }))
63+
U.deepStrictEqual(S1.concat({}, {}), {})
64+
65+
const S2 = _.partial({ a: S.Semigroup, b: N.SemigroupSum, c: B.SemigroupAll, d: S.Semigroup })
66+
U.deepStrictEqual(S2.concat({ a: 'first', b: 12 }, { b: 2, c: true }), { a: 'first', b: 14, c: true })
67+
})
68+
6069
it('semigroupAll', () => {
6170
// tslint:disable-next-line: deprecation
6271
const S = _.semigroupAll

0 commit comments

Comments
 (0)