Created
June 6, 2020 15:11
-
-
Save dfsweeney/1fddddd2e2cb077021ac82e048d5b056 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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