Skip to content

Instantly share code, notes, and snippets.

@jkomyno
Last active September 26, 2024 22:18
Show Gist options
  • Save jkomyno/805765acebee445516a6cfc4befe26a1 to your computer and use it in GitHub Desktop.
Save jkomyno/805765acebee445516a6cfc4befe26a1 to your computer and use it in GitHub Desktop.
Comparison `effect/Match` vs `ts-pattern` for an AST-traversal and execution exercise
import { Data, Order, Equal, Match } from 'effect'
import { match } from 'ts-pattern'
class $String extends Data.TaggedClass('String')<{ value: string }> {}
class $Number extends Data.TaggedClass('Number')<{ value: number }> {}
class $Boolean extends Data.TaggedClass('Boolean')<{ value: boolean }> {}
type $TypedValue = $String | $Number | $Boolean
const orderTypedValue = Order.make<$TypedValue>(
(x, y) =>
Match.value([x, y]).pipe(
Match.when([{ _tag: 'String' }, { _tag: 'String' }], ([x, y]) => Order.string(x.value, y.value)),
Match.when([{ _tag: 'Number' }, { _tag: 'Number' }], ([x, y]) => Order.number(x.value, y.value)),
Match.when([{ _tag: 'Boolean' }, { _tag: 'Boolean' }], ([x, y]) => Order.boolean(x.value, y.value)),
Match.orElse(() => {
throw new Error(`Cannot compare ${x._tag} with ${y._tag}`)
})
)
)
type Expr =
| {
$type: 'StringLiteral'
value: string
}
| {
$type: 'NumberLiteral'
value: number
}
| {
$type: 'ColumnNameExpression'
columnName: { value: string }
}
| {
$type: 'IdentifierAsStringLiteral'
value: { value: string }
}
| {
$type: 'BinaryExpression'
operator: '=' | '<' | '>'
left: Expr
right: Expr
}
function getColumnValue(row: Record<string, unknown>, columnName: string): $TypedValue {
const value = row[columnName]
if (typeof value === 'number') {
return new $Number({ value })
}
if (typeof value === 'string') {
return new $String({ value })
}
throw new Error(`Unknown type for column ${columnName} and value ${value}`)
}
// /* ts-pattern implementation */
function evalExpression(row: Record<string, unknown>, expr: Expr): $TypedValue {
return match(expr)
.with({ $type: 'StringLiteral' }, ({ value }) => new $String({ value }))
.with({ $type: 'NumberLiteral' }, ({ value }) => new $Number({ value }))
.with({ $type: 'ColumnNameExpression' }, ({ columnName }) => getColumnValue(row, columnName.value))
.with({ $type: 'IdentifierAsStringLiteral' }, ({ value }) => new $String({ value: value.value }))
.with({ $type: 'BinaryExpression' }, ({ operator, left, right }) => {
const leftValue = evalExpression(row, left)
const rightValue = evalExpression(row, right)
return match(operator)
.with('=', () => new $Boolean({ value: Equal.equals(leftValue, rightValue) }))
.with('<', () => new $Boolean({ value: Order.lessThan(orderTypedValue)(leftValue, rightValue) }))
.with('>', () => new $Boolean({ value: Order.greaterThan(orderTypedValue)(leftValue, rightValue) }))
.exhaustive()
})
.exhaustive()
}
// /* effect/Match implementation */
// function evalExpression(row: Record<string, unknown>, expr: Expr): $TypedValue {
// return Match.value(expr).pipe(
// Match.when({ $type: 'StringLiteral' }, ({ value }) => new $String({ value })),
// Match.when({ $type: 'NumberLiteral' }, ({ value }) => new $Number({ value })),
// Match.when({ $type: 'ColumnNameExpression' }, ({ columnName }) => getColumnValue(row, columnName.value)),
// Match.when({ $type: 'IdentifierAsStringLiteral' }, ({ value }) => new $String({ value: value.value })),
// Match.when({ $type: 'BinaryExpression' }, ({ operator, left, right }) => {
// const leftValue = evalExpression(row, left)
// const rightValue = evalExpression(row, right)
// return Match.value(operator).pipe(
// Match.when('=', () => new $Boolean({ value: Equal.equals(leftValue, rightValue) })),
// Match.when('<', () => new $Boolean({ value: Order.lessThan(orderTypedValue)(leftValue, rightValue) })),
// Match.when('>', () => new $Boolean({ value: Order.greaterThan(orderTypedValue)(leftValue, rightValue) })),
// Match.exhaustive,
// )
// }),
// Match.exhaustive,
// )
// }
const row = { col1: 0, col2: 'bbb' }
// col1 > "asd"
const expr: Expr = {
$type: 'BinaryExpression',
operator: '<',
left: {
$type: 'ColumnNameExpression',
columnName: { value: 'col1' }
},
right: {
$type: 'NumberLiteral',
value: 1
}
}
console.log('[eval]')
const $type = evalExpression(row, expr)
console.log($type)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment