Skip to content

Commit

Permalink
Merge pull request #4 from ataibarkai/2.0Release/protocolRenaming
Browse files Browse the repository at this point in the history
2.0 release/protocol renaming
  • Loading branch information
ataibarkai authored Nov 13, 2019
2 parents 7882080 + 525304f commit f54cb0b
Show file tree
Hide file tree
Showing 20 changed files with 475 additions and 351 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ let package = Package(
name: "SemanticType",
dependencies: []),
.testTarget(
name: "SemanticType_Tests",
name: "SemanticType-Tests",
dependencies: ["SemanticType"]),
]
)
290 changes: 206 additions & 84 deletions Readme.md

Large diffs are not rendered by default.

22 changes: 12 additions & 10 deletions Sources/SemanticType/CoreStructures/SemanticType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/// the types used to store its relevant state.
/// - Tag: SemanticType
@dynamicMemberLookup
public struct SemanticType<Spec: GeneralizedSemanticTypeSpec> {
public struct SemanticType<Spec: MetaValidatedSemanticTypeSpec> {

public typealias Spec = Spec

Expand All @@ -30,15 +30,16 @@ public struct SemanticType<Spec: GeneralizedSemanticTypeSpec> {
/// under *all* circumstances.
private let gatewayOutput: Spec.GatewayOutput

/// A proxy internally exposing the backingPrimitive portion of the `gatewayOutput` property to other files in this package.
/// A proxy internally exposing the `rawValue` portion of the `gatewayOutput` property to other files in this package.
///
/// We define it as an underscore-prefixed, internal variable so that we can define a corresponding
/// variable which has both a getter and a setter under some conditional extensions, but only a getter otherwise.
internal var _backingPrimitive: Spec.BackingPrimitiveWithValueSemantics {
gatewayOutput.backingPrimitvie
/// We define it as an underscore-prefixed, internal variable so that we can define a corresponding public
/// variable which has **both** a getter and a setter under some conditional extensions, but only a getter otherwise.
internal var _rawValue: Spec.RawValue {
gatewayOutput.rawValue
}

public var gatewayMetadata: Spec.GatewayMetadataWithValueSemantics {
/// The metadata value outputted by the gateway function, along with the (possibly transformed) `rawValue`.
public var gatewayMetadata: Spec.Metadata {
gatewayOutput.metadata
}

Expand All @@ -54,16 +55,17 @@ public struct SemanticType<Spec: GeneralizedSemanticTypeSpec> {
}

/// The `SemanticType` factory through which all paths of `SemanticType` creation must pass.
/// An obtained `SemanticType` is guarenteed to respect its `Spec.gateway` function, in that
/// its backing primitive is guarenteed to have been the output of a call to `Spec.gateway` using the given input.
///
/// The obtained `SemanticType` is guarenteed to respect its `Spec.gateway` function:
/// its `rawValue` is guarenteed to have been the output of a call to `Spec.gateway` using the given input.
///
/// - Parameter preMap: The value to be passed through the `Spec.gatemapMap` function.
///
/// - Returns: A `SemanticType` instance wrapping the `.success` output of the `Sepc.gateway` function,
/// or otherwise, the error captured by a `.failure` output of the `Sepc.gateway` function.
///
/// - Tag: create
public static func create(_ preMap: Spec.BackingPrimitiveWithValueSemantics) -> Result<Self, Spec.Error> {
public static func create(_ preMap: Spec.RawValue) -> Result<Self, Spec.Error> {
return Spec
.gateway(preMap: preMap)
.map(Self.init(_unsafeDirectlyAssignedBackingPrimitive:))
Expand Down
87 changes: 49 additions & 38 deletions Sources/SemanticType/CoreStructures/SemanticTypeSpec.swift
Original file line number Diff line number Diff line change
@@ -1,112 +1,123 @@
/// A specification object statically determining the properties of a `SemanticType` object
/// associated with it.
public protocol GeneralizedSemanticTypeSpec {
public protocol MetaValidatedSemanticTypeSpec {

/// The type of the primitive value wrapped by the `SemanticType`.
/// Must possess value semantics.
///
/// The backing primitive must possess *value semantics* to insure that the structure of a value is
/// `RawValue` must possess *value semantics* to insure that the structure of a value is
/// not modified by outside forces after passing through the `SemanticType`'s `gateway` function
/// and becoming stored inside of a `SemanticType` instance.
associatedtype BackingPrimitiveWithValueSemantics
associatedtype RawValue

/// The type of the gateway-associated metadata available on the `SemanticType`.
/// Must possess value semantics.
///
/// The metadata must possess *value semantics* to ensure that the structure of a value is
/// not modified by outside forces after passing through the `SemanticType`'s `gateway` function
/// and becoming stored inside of a `SemanticType` instance.
///
/// ---
///
/// The metadata value is stored on the `SemanticType` instance and is publically available for access.
/// It may be used to encode a compiler-accessible fascet of the sub-structure of the wrapped value
/// which was veriried by the `gatewayMap` function.
/// The `gatewayMap` function may verify that the internal sub-structure of a returned `RawValue`
/// satisfies any number of constraints.
/// `Metadata` encodes a **compiler-visible fascet** of said verified sub-structure.
///
/// ---
///
/// As an example: suppose we create a `NonEmptyArray` `SemanticType`, i.e.
/// a type whose instances wrap an `Array` -- but which could only be created when said `Array` is non-empty.
/// a type whose instances wrap an `Array` which is *always* non-empty.
///
/// Unlike instances of `Array`, instances of `NonEmptyArray` are **guarenteed** to have `first` and `last` elements.
/// Thus we may expose `first: Element` and `last: Element` in place of `Array`'s corresponding *optional* properties.
///
/// Since we know that instances of `NonEmptyArray` are not empty, we _could_ implement said non-optionoal
/// `first` and `last` overrides by forwarding the call to `Array`'s optional properties and force-unwrapping the result.
///
/// While *we* know this process ought to work, the *compiler* does not -- hence the need for the force-unwrapping.
/// And so we lose the celebrated compiler verification normally characterizing idiomatic swift code.
///
/// Instead, we could implement `first` and `last` without circumventing compiler verifications by storing the `Array`'s `first` and `last`
/// values as *metadata* during the `gatewayMap`ing (where we could return an error if `first` and `last` are not available).
/// Then the non-optional `first` and `last` properties could be implemented by querying said metadata values.
associatedtype GatewayMetadataWithValueSemantics
/// The non-optional `first` and `last` properties on `NonEmptyArray` could then be implemented by querying said metadata values.
associatedtype Metadata

/// The type of the error which could be returned when attempting to create instance of the `SemanticType`.
associatedtype Error: Swift.Error

/// The output of the [gatewayMap function](x-source-tag://SemanticTypeSpec.gateway).
/// See additional documentation on the [struct definition](x-source-tag://GeneralizedSemanticTypeSpec_GatewayOutput)
/// See additional documentation on the [struct definition](x-source-tag://MetaValidatedSemanticTypeSpec_GatewayOutput)
///
/// - Tag: GeneralizedSemanticTypeSpec.GatewayOutput
typealias GatewayOutput = GeneralizedSemanticTypeSpec_GatewayOutput<BackingPrimitiveWithValueSemantics, GatewayMetadataWithValueSemantics>
/// - Tag: MetaValidatedSemanticTypeSpec.GatewayOutput
typealias GatewayOutput = MetaValidatedSemanticTypeSpec_GatewayOutput<RawValue, Metadata>

/// A function gating the creation of all `SemanticType` instances associated with this Spec.
///
/// - Parameter preMap: The primitive value to be analyzed and modified for association with a `SemanticType` instance.
/// - Returns: If a `SemanticType` instance should be created given the provided input, returns the backing primitive
/// - Parameter preMap: The `RawValue` to be analyzed and modified for association with a `SemanticType` instance.
/// - Returns: If a `SemanticType` instance should be created given the provided input, returns the (possibly transformed) `RawValue` to back the created `SemanticType` instance. Otherwise, returns the error specifying why the instance cannot be creted given the provided input.
/// - Tag: SemanticTypeSpec.gateway
static func gateway(
preMap: BackingPrimitiveWithValueSemantics
preMap: RawValue
) -> Result<GatewayOutput, Error>
}
/// The output of the [gatewayMap function](x-source-tag://SemanticTypeSpec.gateway).
///
///
/// NOTE: Since Swift does not currently support nesting definitions of types nested inside a protocol,
/// we utilize a naming convention + [a typealias on the target protocol](x-source-tag://GeneralizedSemanticTypeSpec.GatewayOutput)
/// we utilize a naming convention + [a typealias on the target protocol](x-source-tag://MetaValidatedSemanticTypeSpec.GatewayOutput)
/// to effectively achieve this goal.
/// In other words, this type should be viewed as if it were nested under the `GeneralizedSemanticTypeSpec` protocol.
/// In other words, this type should be viewed as if it were nested under the `MetaValidatedSemanticTypeSpec` protocol.
///
/// - Tag: GeneralizedSemanticTypeSpec_GatewayOutput
public struct GeneralizedSemanticTypeSpec_GatewayOutput<BackingPrimitiveWithValueSemantics, GatewayMetadataWithValueSemantics> {
/// - Tag: MetaValidatedSemanticTypeSpec_GatewayOutput
public struct MetaValidatedSemanticTypeSpec_GatewayOutput<RawValue, Metadata> {

/// The primitive value to back a succesfully-created `SemanticType` instance.
/// The behavior of the `SemanticType` manifestation largely revolves around this type
/// The behavior of the `SemanticType` construct largely revolves around this field.
/// (see [SemanticType](x-source-tag://SemanticType)).
var backingPrimitvie: BackingPrimitiveWithValueSemantics
var rawValue: RawValue

/// Additinoal metadata object available to the successfully-created `SemanticType` instance.
/// Additinoal metadata available to the successfully-created `SemanticType` instance.
/// May be utilized to provide compiler-verified extensions on the SemanticType, taking advantage of the
/// constraints satisfied by the distilled primitive.
var metadata: GatewayMetadataWithValueSemantics
/// constraints satisfied by the distilled `RawValue`.
var metadata: Metadata
}


/// A `SemanticTypeSpec` with no gateway metadata.
public protocol SemanticTypeSpec: GeneralizedSemanticTypeSpec where GatewayMetadataWithValueSemantics == () {
public protocol ValidatedSemanticTypeSpec: MetaValidatedSemanticTypeSpec where Metadata == () {
static func gateway(
preMap: BackingPrimitiveWithValueSemantics
) -> Result<BackingPrimitiveWithValueSemantics, Error>
preMap: RawValue
) -> Result<RawValue, Error>
}
extension SemanticTypeSpec {
extension ValidatedSemanticTypeSpec {
// Implement the parent protocol's requirements in terms of `Self's` requirements:
public static func gateway(
preMap: BackingPrimitiveWithValueSemantics
preMap: RawValue
) -> Result<GatewayOutput, Error> {
return gateway(preMap: preMap)
.map { .init(backingPrimitvie: $0, metadata: ()) }
.map { .init(rawValue: $0, metadata: ()) }
}
}

/// A `SemanticTypeSpec` whose `gateway` function never errors.
public protocol ErrorlessSemanticTypeSpec: SemanticTypeSpec where Error == Never {
public protocol ErrorlessSemanticTypeSpec: ValidatedSemanticTypeSpec where Error == Never {
static func gateway(
preMap: BackingPrimitiveWithValueSemantics
) -> BackingPrimitiveWithValueSemantics
preMap: RawValue
) -> RawValue
}
extension ErrorlessSemanticTypeSpec {
// Implement the parent protocol's requirements in terms of `Self's` requirements:
public static func gateway(
preMap: BackingPrimitiveWithValueSemantics
) -> Result<BackingPrimitiveWithValueSemantics, Error> {
preMap: RawValue
) -> Result<RawValue, Error> {
return .success(gateway(preMap: preMap))
}

// By default, `ErrorlessSemanticTypeSpec` uses the identity function as a gateway
// (i.e. no transformation is performed on `RawValue`).
public static func gateway(
preMap: BackingPrimitiveWithValueSemantics
) -> BackingPrimitiveWithValueSemantics {
preMap: RawValue
) -> RawValue {
return preMap
}
}

Original file line number Diff line number Diff line change
@@ -1,50 +1,50 @@
/// A marker protocol to be conformed to by a `SemanticTypeSpec` type
/// to conditionally provide `Numeric` support for its associated `SemanticType`.
///
/// A marker protocol.
/// If a `SemanticTypeSpec` conforms to this protocol, its associated `SemanticType`
/// will conform to `Numeric`.
public protocol ShouldBeNumeric: GeneralizedSemanticTypeSpec
///
/// `Numeric` support may not make sense for all
/// `SemanticType`s associated with a `Numeric` `RawValue`
/// (for instance, [`Second` * `Second` = `Second`] does not make semantic sense).
/// Nevertheless, we *can* provide an implementation in all such cases.
///
/// We allow the `Spec` backing the `SemanticType` to signal whether `Numeric`
/// support should be provided by conforming to the `ShouldBeNumeric` marker protocol.
public protocol ShouldBeNumeric: MetaValidatedSemanticTypeSpec
where
BackingPrimitiveWithValueSemantics: Numeric { }
RawValue: Numeric { }

extension SemanticType: Numeric
where
Spec: ShouldBeNumeric, // `Numeric` support may not make sense for all
// `SemanticType`s associated with a `Numeric` `BackingPrimitiveWithValueSemantics`
// (for instance, [`Second` * `Second` = `Second`] does not make semantic sense).
// Nevertheless, we *can* provide an implementation in all such cases.
//
// We allow the `Spec` backing the `SemanticType` to signal whether `Numeric`
// support should be provided by conforming to the `ShouldBeNumeric` marker protocol.
Spec: ShouldBeNumeric,
Spec.Error == Never
{
public typealias Magnitude = Spec.BackingPrimitiveWithValueSemantics.Magnitude
public typealias Magnitude = Spec.RawValue.Magnitude

public init?<T>(exactly source: T) where T : BinaryInteger {
guard let inside = Spec.BackingPrimitiveWithValueSemantics.init(exactly: source)
guard let inside = Spec.RawValue.init(exactly: source)
else { return nil }

self.init(inside)
}

public var magnitude: Spec.BackingPrimitiveWithValueSemantics.Magnitude {
backingPrimitive.magnitude
public var magnitude: Spec.RawValue.Magnitude {
rawValue.magnitude
}

public static func * (lhs: Self, rhs: Self) -> Self {
Self(lhs.backingPrimitive * rhs.backingPrimitive)
Self(lhs.rawValue * rhs.rawValue)
}

public static func *= (lhs: inout Self, rhs: Self) {
lhs.backingPrimitive *= rhs.backingPrimitive
lhs.rawValue *= rhs.rawValue
}
}


extension SemanticType: SignedNumeric
where
Spec: ShouldBeNumeric,
Spec.BackingPrimitiveWithValueSemantics: SignedNumeric,
Spec.RawValue: SignedNumeric,
Spec.Error == Never
{ }

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
extension SemanticType: ExpressibleByIntegerLiteral
where
Spec.BackingPrimitiveWithValueSemantics: ExpressibleByIntegerLiteral,
Spec.RawValue: ExpressibleByIntegerLiteral,
Spec.Error == Never
{
public typealias IntegerLiteralType = Spec.BackingPrimitiveWithValueSemantics.IntegerLiteralType
public typealias IntegerLiteralType = Spec.RawValue.IntegerLiteralType

public init(integerLiteral value: Self.IntegerLiteralType) {
self.init(
Expand All @@ -14,10 +14,10 @@ extension SemanticType: ExpressibleByIntegerLiteral

extension SemanticType: ExpressibleByFloatLiteral
where
Spec.BackingPrimitiveWithValueSemantics: ExpressibleByFloatLiteral,
Spec.RawValue: ExpressibleByFloatLiteral,
Spec.Error == Never
{
public typealias FloatLiteralType = Spec.BackingPrimitiveWithValueSemantics.FloatLiteralType
public typealias FloatLiteralType = Spec.RawValue.FloatLiteralType

public init(floatLiteral value: Self.FloatLiteralType) {
self.init(
Expand All @@ -28,10 +28,10 @@ extension SemanticType: ExpressibleByFloatLiteral

extension SemanticType: ExpressibleByUnicodeScalarLiteral
where
Spec.BackingPrimitiveWithValueSemantics: ExpressibleByUnicodeScalarLiteral,
Spec.RawValue: ExpressibleByUnicodeScalarLiteral,
Spec.Error == Never
{
public typealias UnicodeScalarLiteralType = Spec.BackingPrimitiveWithValueSemantics.UnicodeScalarLiteralType
public typealias UnicodeScalarLiteralType = Spec.RawValue.UnicodeScalarLiteralType

public init(unicodeScalarLiteral value: Self.UnicodeScalarLiteralType) {
self.init(
Expand All @@ -42,10 +42,10 @@ extension SemanticType: ExpressibleByUnicodeScalarLiteral

extension SemanticType: ExpressibleByExtendedGraphemeClusterLiteral
where
Spec.BackingPrimitiveWithValueSemantics: ExpressibleByExtendedGraphemeClusterLiteral,
Spec.RawValue: ExpressibleByExtendedGraphemeClusterLiteral,
Spec.Error == Never
{
public typealias ExtendedGraphemeClusterLiteralType = Spec.BackingPrimitiveWithValueSemantics.ExtendedGraphemeClusterLiteralType
public typealias ExtendedGraphemeClusterLiteralType = Spec.RawValue.ExtendedGraphemeClusterLiteralType

public init(extendedGraphemeClusterLiteral value: Self.ExtendedGraphemeClusterLiteralType) {
self.init(
Expand All @@ -56,10 +56,10 @@ extension SemanticType: ExpressibleByExtendedGraphemeClusterLiteral

extension SemanticType: ExpressibleByStringLiteral
where
Spec.BackingPrimitiveWithValueSemantics: ExpressibleByStringLiteral,
Spec.RawValue: ExpressibleByStringLiteral,
Spec.Error == Never
{
public typealias StringLiteralType = Spec.BackingPrimitiveWithValueSemantics.StringLiteralType
public typealias StringLiteralType = Spec.RawValue.StringLiteralType

public init(stringLiteral value: Self.StringLiteralType) {
self.init(
Expand All @@ -70,10 +70,10 @@ extension SemanticType: ExpressibleByStringLiteral

extension SemanticType: ExpressibleByBooleanLiteral
where
Spec.BackingPrimitiveWithValueSemantics: ExpressibleByBooleanLiteral,
Spec.RawValue: ExpressibleByBooleanLiteral,
Spec.Error == Never
{
public typealias BooleanLiteralType = Spec.BackingPrimitiveWithValueSemantics.BooleanLiteralType
public typealias BooleanLiteralType = Spec.RawValue.BooleanLiteralType

public init(booleanLiteral value: Self.BooleanLiteralType) {
self.init(
Expand Down
Loading

0 comments on commit f54cb0b

Please sign in to comment.