Skip to content

Instantly share code, notes, and snippets.

@dfsweeney
Created June 6, 2020 15:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfsweeney/1fddddd2e2cb077021ac82e048d5b056 to your computer and use it in GitHub Desktop.
Save dfsweeney/1fddddd2e2cb077021ac82e048d5b056 to your computer and use it in GitHub Desktop.
//
// Structured.swift
//
// Created by daniel sweeney on 6/5/20.
// Copyright © 2020 daniel sweeney. All rights reserved.
//
import Foundation
// MARK: ------ Basic Structure ------
/// A 3-element structure. You'd need to generate a bunch of these for various-size
/// structs, and then keep the representation in sync in the .structure method
/// if you could iterate on keypaths ._1 ... ._n you might be able to get around some of
/// that. I'm not convinced that variadic generics would address this.
/// I just picked 3 arbitrarily for this example. The boilerplate follows for different sizes.
/// if the compiler could pick this that would be nice
struct Structure3<Type_1, Type_2, Type_3> {
let _1 : Type_1
let _2 : Type_2
let _3 : Type_3
}
/// Protocol to say you can return your structure
/// The StructureWrapper only has one type
/// You would have to change to a StructureN depending how much structure
/// the struct has, and how much you want to expose
protocol HasStructure {
associatedtype CompositeType
var structure: StructureWrapper<CompositeType> {get}
}
/// A wrapper to hold a single type
/// the InnerType will probably be composite like Structure3, etc
/// This is what the .structure property returns
/// you get one level of indirection on the type here
struct StructureWrapper<CompositeType> {
let innerType : CompositeType
}
/// individual property
struct Property<PropertyType> {
let value: PropertyType
}
// MARK: ------ Make everything CustomHashable ------
/// The CustomHashable protocol and customHash function
/// and the comment below are from
/// Sources/StructuralExamples/CustomHashable.swift in
/// https://github.com/google/swift-structural/master/
///
/// A duplicate, simplified version of the `Hashable` protocol.
/// - Note: a duplicate protocol is used to avoid triggering existing `Equatable` derived
/// conformances.
public protocol CustomHashable {
func customHash(into hasher: inout Hasher)
}
public func customHash<T>(_ value: T) -> Int where T: CustomHashable {
var hasher = Hasher()
value.customHash(into: &hasher)
return hasher.finalize()
}
/// Add customHash to every HasStructure where the inner type is CustomHashable
extension CustomHashable where Self: HasStructure,
Self.CompositeType : CustomHashable {
func customHash(into hasher: inout Hasher) {
/// delegates to structure
return self.structure.customHash(into: &hasher)
}
}
/// make .structure CustomHashable
/// delegates to the innerType
/// the innerType is probably composite
extension StructureWrapper: CustomHashable
where CompositeType: CustomHashable {
func customHash(into hasher: inout Hasher) {
self.innerType.customHash(into: &hasher)
}
}
/// make Structure3 CustomHashable when its components are
extension Structure3 : CustomHashable where Type_1: CustomHashable,
Type_2: CustomHashable,
Type_3: CustomHashable {
func customHash(into hasher: inout Hasher) {
///This is a little clunky. You'd like to have some kind of
/// allKeypaths or something
/// and maybe not have to do the type erasure
/// etc.
/// obviously this is hard to generalize.
let keypaths : [PartialKeyPath<Structure3>] = [\Structure3._1,
\Structure3._2,
\Structure3._3]
for keypath in keypaths {
// downcasting is bad
let thisProperty = self[keyPath: keypath] as! CustomHashable
thisProperty.customHash(into:&hasher)
}
}
}
/// if PropertyType is CustomHashable then so is the Property
extension Property : CustomHashable where PropertyType: CustomHashable {
func customHash(into hasher: inout Hasher) {
self.value.customHash(into: &hasher)
}
}
/// extensions to Int, String, Double to be CustomHashable
extension Int: CustomHashable {
public func customHash(into hasher: inout Hasher) {
self.hash(into: &hasher)
}
}
extension String: CustomHashable {
public func customHash(into hasher: inout Hasher) {
self.hash(into: &hasher)
}
}
extension Double: CustomHashable {
public func customHash(into hasher: inout Hasher) {
self.hash(into: &hasher)
}
}
// MARK: ------ Example Structure -------
struct Structured : CustomHashable {
let a : String
let b : Int
let c : Double
}
/// Add HasStructure and return structure.
extension Structured : HasStructure {
typealias CompositeType =
Structure3<Property<String>,
Property<Int> ,
Property<Double>>
var structure : StructureWrapper<CompositeType> {get
{
StructureWrapper(innerType: Structure3(_1: Property(value: a),
_2: Property(value: b),
_3: Property(value: c)))
}
}
}
/// Don't need to add CustomHashable because Structured will
/// get it from the extension CustomHashable
/// where Self: HasStructure,
/// Self.CompositeType : CustomHashable
//extension Structured: CustomHashable {
// func customHash(into hasher: inout Hasher) {
// self.structure.customHash(into: &hasher)
// }
//}
// MARK: ------ test method ------
func structural() {
let aStructured = Structured(a: "this", b: 4, c: 2.7)
print(aStructured)
print(aStructured.structure)
print(customHash(aStructured))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment