[swift-users] custom binding pattern matches

David Young dyoung+swift at pobox.com
Tue Aug 15 21:02:21 CDT 2017


Sometimes I wish for custom *binding* patterns in Swift.  Is something
like this already in the works?  Anyone else wishing for this?

I have attached some code where I fake it, but these examples
show what I'm really after:

/* Match complex numbers, let expressions with type qualification: */

for elt in [1.0 + î, 2.0 + 3.0 * î, 4.0 + 5.0 * î, 6.0 * î] {
	switch elt {
	case î * (let d: Double):
		print("matched î * \(d)")
	case 1 + î * (let a: Double):
		print("matched 1 + î * \(a)")
	case (let b: Double) + î * (let c: Double):
		print("matched \(b) + î * \(c)")
	default:
		print("no match")
	}
}

/* Match complex numbers, let expressions with types inferred: */

for elt in [1.0 + î, 2.0 + 3.0 * î, 4.0 + 5.0 * î, 6.0 * î] {
	switch elt {
	case î * (let d):
		print("matched î * \(d)")
	case 1 + î * (let a):
		print("matched 1 + î * \(a)")
	case (let b) + î * (let c):
		print("matched \(b) + î * \(c)")
	default:
		print("no match")
	}
}

/* Match strings, assigning substrings to numeric types a la scanf(3): */

if case "\(let a: Double) + \(let b: Double) î" = "1 + 2 î"  {
	print("matched \(a) + \(b) î")
}

/* Approximately match strings; `fuzz` is an "edit distance" between
 * the pattern and the match.
 */ 

if case ApproximateMatch("\(let a: Double) + \(let b: Double) î", let fuzz) = "1 + 2 * î", fuzz < 5 {
	print("matched \(a) + \(b) î with fuzz \(d)")
}

/* Simplify an algebraic expression: n1 / d + n2 / d -> (n1 + n2) / d.
 *
 * Original source:
 */

if case .sum(.quotient(let ln, let ld),
             .quotient(let rn, let rd)) = expr, ld == rd {
	return evaluate(.quotient(.sum(ln, rn), ld), env)
}

/* => with operator overloading (no custom binding patterns) becomes: */

if case .sum(.quotient(let ln, let ld),
             .quotient(let rn, let rd)) = expr, ld == rd {
	return evaluate((ln + rn) / ld, env)
}

/* => with custom binding patterns and operator overloading becomes: */

if case (let ln: Expr) / (let ld: Expr) +
        (let rn: Expr) / (let rd: Expr) = expr, ld == rd {
	return evaluate((ln + rn) / ld, env)
}

/* => with type inference becomes: */

if case (let ln) / (let ld) + (let rn) / (let rd) = expr, ld == rd {
	return evaluate((ln + rn) / ld, env)
}

Dave

-- 
David Young
dyoung at pobox.com    Urbana, IL    (217) 721-9981
-------------- next part --------------
enum Binds<T : Equatable> {
case constant(T)
case bind((T) -> Bool)
case matchless
}

extension Binds : CustomStringConvertible {
	var description: String {
		switch self {
		case .bind(_):
			return ".bind(_)"
		case .constant(let x):
			return ".constant(\(x))"
		case .matchless:
			return ".matchless"
		}
	}
}

func ~=<T>(pattern: Binds<T>, value: T) -> Bool {
	switch pattern {
	case .bind(let bind):
		return bind(value)
	case .constant(value):
		return true
	default:
		return false
	}
}
-------------- next part --------------
/* a + b î */
struct Complex<A, B> {
	let a: A
	let b: B
	init(_ a: A, _ b: B) {
		self.a = a
		self.b = b
	}
}

let î = Complex<Double, Double>(0, 1)

func ~=<V>(pattern: Complex<Binds<V>, Binds<V>>, value: Complex<V, V>) -> Bool {
	return pattern.a ~= value.a && pattern.b ~= value.b
}

func +(l: Complex<Binds<Double>, Binds<Double>>,
       r: Complex<Binds<Double>, Binds<Double>>)
    -> Complex<Binds<Double>, Binds<Double>> {
	return Complex(l.a + r.a, r.a + r.b)
}

func *(l: Complex<Binds<Double>, Binds<Double>>,
       r: Complex<Binds<Double>, Binds<Double>>)
    -> Complex<Binds<Double>, Binds<Double>> {
	return Complex(l.a * r.a - l.b * r.b, l.a * r.b + l.b * r.a)
}

func +(l: Binds<Double>, r: Complex<Binds<Double>, Binds<Double>>)
    -> Complex<Binds<Double>, Binds<Double>> {
	return Complex(l + r.a, r.b)
}

func +(l: Double, r: Complex<Binds<Double>, Binds<Double>>)
    -> Complex<Binds<Double>, Binds<Double>> {
	return Complex(l + r.a, r.b)
}

func *(l: Double, r: Complex<Double, Double>)
    -> Complex<Double, Double> {
	return Complex(l * r.a, l * r.b)
}

func +(l: Double, r: Complex<Double, Double>)
    -> Complex<Double, Double> {
	return Complex(l + r.a, r.b)
}

func +(l: Float, r: Complex<Double, Double>)
    -> Complex<Double, Double> {
	return Complex(Double(l) + r.a, r.b)
}

func *(l: Complex<Double, Double>, r: Binds<Double>)
    -> Complex<Binds<Double>, Binds<Double>> {
	return Complex(l.a * r, l.b * r)
}

-------------- next part --------------
func *(l: Binds<Double>, r: Binds<Double>) -> Binds<Double> {
	switch (l, r) {
	case (.constant(let x), .constant(let y)):
		return .constant(x * y)
	case (.constant(0), _), (_, .constant(0)):
		return .constant(0)
	case (.constant(1), _):
		return r
	case (_, .constant(1)):
		return l
	default:
		return .matchless
	}
}

func -(l: Binds<Double>, r: Binds<Double>) -> Binds<Double> {
	switch (l, r) {
	case (.constant(let x), .constant(let y)):
		return .constant(x - y)
	case (.constant(0), .bind(let f)):
		return .bind({ x in return f(-x) })
	case (.constant(0), _):
		return r
	case (_, .constant(0)):
		return l
	default:
		return .matchless
	}
}

func +(l: Binds<Double>, r: Binds<Double>) -> Binds<Double> {
	switch (l, r) {
	case (.constant(let x), .constant(let y)):
		return .constant(x + y)
	case (.constant(0), _):
		return r
	case (_, .constant(0)):
		return l
	default:
		return .matchless
	}
}

func *(l: Double, r: Binds<Double>) -> Binds<Double> {
	switch l {
	case 0:
		return .constant(0)
	case 1:
		return r
	default:
		break
	}
	switch r {
	case .constant(let x):
		return .constant(l * x)
	case .bind(let f):
		return .bind({ x in return f(x / l) })
	default:
		return .matchless
	}
}

func +(l: Double, r: Binds<Double>) -> Binds<Double> {
	switch r {
	case .constant(let x):
		return .constant(l + x)
	case .bind(let f):
		return .bind({ x in return f(x - l) })
	default:
		return .matchless
	}
}
-------------- next part --------------
var a: Double = 0
var b: Double = 0
var c: Double = 0
var d: Double = 0

let leta: Binds<Double> = .bind({ x in a = x; return true })
let letb: Binds<Double> = .bind({ x in b = x; return true })
let letc: Binds<Double> = .bind({ x in c = x; return true })
let letd: Binds<Double> = .bind({ x in d = x; return true })

for elt in [1.0 + î, 2.0 + 3.0 * î, 4.0 + 5.0 * î, 6.0 * î] {
	switch elt {
	case î * letd:
		print("matched î * \(d)")
	case 1 + î * leta:
		print("matched 1 + î * \(a)")
	case letb + î * letc:
		print("matched \(b) + î * \(c)")
	default:
		print("no match")
	}
}

if case 1 + î * leta = 1.0 + î {
	print("match!")
}


More information about the swift-users mailing list