Access Computed Property of a Protocol in a Protocol Extension - swift

Assume I have a protocol like
protocol TypedString : CustomStringConvertible, Equatable, Hashable { }
func == <S : TypedString, T : TypedString> (lhs : T, rhs : S) -> Bool {
return lhs.description == rhs.description
}
I want to be able to provide a default implementation of Hashable:
extension TypedString {
var hashValue = { return self.description.hashValue }
}
But the problem is that I get an error Use of unresolved identifier self.
How do I create a default implementation of Hashable using the string representation given by the description property defined in the CustomStringConvertible protocol?
The motivation is I want to create shallow wrappers around Strings, so I can semantically type-check my code. For example instead of writing func login (u : String, p : String) -> Bool I would instead write func login (u : UserName, p : Password) -> Bool where the (failable) initialisers for UserName and Password do validation. For example I might write
struct UserName : TypedString {
let description : String
init?(userName : String) {
if userName.isEmpty { return nil; }
self.description = userName
}
}

What you want is totally possible, the error message you're receiving just isn't making it very clear what the problem is: your syntax for a computed property is incorrect. Computed properties need to be explicitly typed and don't use an equals sign:
extension TypedString {
var hashValue: Int { return self.description.hashValue }
}
This works fine!

Related

Defining concrete associatedtype but I still can't use protocol as variable

I would like to use SendMessageUseCase as a variable type, for example var sendMessageUseCase: SendMessageUseCase but I can't.
I keep getting the error Protocol 'SendMessageUseCase' can only be used as a generic constraint because it has Self or associated type requirements
Which obviously I understand the error but it doesn't make sense.
In SendMessageUseCase I have defined my associated type using Where clause which means technically the compiler should be able to infer, since I have explicitly defined Input and Output to be SendMessageUseCaseInput and SendMessageUseCaseOutput respectively.
protocol UseCaseInput {}
protocol UseCaseOutput {}
protocol UseCase : AnyObject {
associatedtype Input: UseCaseInput
associatedtype Output: UseCaseOutput
func execute(input: Input, _ completion: (_ output: Output) -> ())
}
struct SendMessageUseCaseInput : UseCaseInput {
var text: String
}
struct SendMessageUseCaseOutput: UseCaseOutput {
var response: String
}
protocol SendMessageUseCase: UseCase where Input == SendMessageUseCaseInput, Output == SendMessageUseCaseOutput {
}
final class TestSendMessageUseCase : SendMessageUseCase {
func execute(input: SendMessageUseCaseInput, _ completion: () -> ()) {
}
}
// Why this doesn't work?
var sendMessageUseCase: SendMessageUseCase = TestSendMessageUseCase()
can SendMessageUseCase.Input be anything else other than SendMessageUseCaseInput? I don't think so which is why I think it should be inferd. But obviously I am wrong and can't understand why.
Thanks to a comment from #Jessy I was able to come to a temporary solution.
Adding some to the sendMessageUseCase variable would allow us to use opaque type but there is still a limitation as you can not have an array.
There are some talks to implement this but as of now (May, 28 2022) it still hasn't been implemented.
protocol UseCaseInput {}
protocol UseCaseOutput {}
protocol UseCase : AnyObject {
associatedtype Input: UseCaseInput
associatedtype Output: UseCaseOutput
func execute(input: Input, _ completion: (_ output: Output) -> ())
}
struct SendMessageUseCaseInput : UseCaseInput {
var text: String
}
struct SendMessageUseCaseOutput: UseCaseOutput {
var response: String
}
protocol SendMessageUseCase: UseCase where Input == SendMessageUseCaseInput, Output == SendMessageUseCaseOutput {
}
final class TestSendMessageUseCase : SendMessageUseCase {
func execute(input: SendMessageUseCaseInput, _ completion: (SendMessageUseCaseOutput) -> ()) {
}
}
var sendMessageUseCase: some SendMessageUseCase = TestSendMessageUseCase()
If anyone is interested here are the forum threads
Lifting the “Self or associated type” constraint on existentials
[ [Sema]AST][WIP] Support existentials with concrete assoc. types #21576 (PR is closed)

Conditional Protocol Conformance to Protocol

I'm learning Swift coming from other languages with powerful type systems and I am wondering if their is any way to make a protocol conditionally conform to another protocol.
Let's take an example: I define a ShowableAsInt protocol, that allows to get an Int representation for any type that conforms to it.
protocol ShowableAsInt {
func intRepr() -> Int
}
extension Int : ShowableAsInt {
func intRepr() -> Int {
return self
}
}
extension String : ShowableAsInt {
func intRepr() -> Int {
return self.count
}
}
func show(_ s: ShowableAsInt) {
print(s.intRepr())
}
show("abc") // "3"
show(42) // "42"
Now I define a Container protocol that simply wraps an element.
protocol Container {
associatedtype T
var element: T {
get
}
}
struct StringContainer : Container {
typealias T = String
let element: String
}
struct IntContainer : Container {
typealias T = Int
let element: Int
}
Because Container is a simple wrapper, whenever the wrapped type can be shown as an Int, the container can also be shown as an Int. I tried to express this, but failed:
// Doesn't compile: can't extend a protocol to conform to another one?
extension Container : ShowableAsInt where T : ShowableAsInt {
func intRepr() -> Int {
return element.intRepr()
}
}
// I would like these to compile
show(IntContainer(42)) // "42"
show(StringContainer("abc")) // "3"
I know that this conditional conformance can be expressed on class and struct. Is there any way to do the same for protocols? If not, is there any reason for this restriction?
The reason why this is not allowed is stated here:
This protocol extension would make any Collection of Equatable elements Equatable, which is a powerful feature that could be put to good use. Introducing conditional conformances for protocol extensions would exacerbate the problem of overlapping conformances, because it would be unreasonable to say that the existence of the above protocol extension means that no type that conforms to Collection could declare its own conformance to Equatable, conditional or otherwise.
See also this question.
If you just don't want to write the duplicate implementation of intRepr everytime, you can do this:
struct StringContainer : ShowableAsIntContainer {
typealias T = String
let element: String
}
struct IntContainer : ShowableAsIntContainer {
typealias T = Int
let element: Int
}
extension Container where T : ShowableAsInt {
func intRepr() -> Int {
return element.intRepr()
}
}
typealias ShowableAsIntContainer = ShowableAsInt & Container

Swift Generic Protocol Function Parameters

This seems like it should work to me. All I am trying to do is make the Rule protocol able to performRule on whatever struct adopts that Rule protocol and then return a boolean. However, with the way my code is currently I cannot access any properties on the performRule(:value) value parameter. I feel like I am missing an important concept or something is buggy. You should be able to copy the code below into a playground to see the issue for yourself.
import Foundation
protocol NumberCalculation {
var number : NSNumber { get set }
}
protocol Rule {
var invalidMessage : String { get set }
func performRule<T>(value: T) -> Bool
}
struct GreaterThanRule : Rule, NumberCalculation {
var invalidMessage: String
var number : NSNumber
init(valueMustBeGreaterThan value: NSNumber, withInvalidMessage message : String = "") {
number = value
invalidMessage = message
}
func performRule<NSNumber>(value: NSNumber) -> Bool {
number.decimalValue // works
value.decimalValue // doesn't work
return true
}
}
Saying <NSNumber> defines a new generic placeholder type in your performRule(value:) method, which, as you've named it NSNumber, will shadow Foundation's NSNumber class – meaning that the value: parameter is of type your generic placeholder, not Foundation's NSNumber.
If you want it so that types conforming to Rule can choose their own type for the parameter of the performRule(value:) method – then you want an associated type, not a generic placeholder.
protocol NumberCalculation {
var number : NSNumber { get set }
}
protocol Rule {
// define associated type that conforming types must satisfy
// by providing a type to replace it with
associatedtype Value
var invalidMessage : String { get set }
func performRule(value: Value) -> Bool
}
struct GreaterThanRule : Rule, NumberCalculation {
var invalidMessage: String
var number : NSNumber
init(valueMustBeGreaterThan value: NSNumber, withInvalidMessage message : String = "") {
number = value
invalidMessage = message
}
// satisfy associated type implicitly by annotating the type of the parameter
// as NSNumber – the compiler will infer that Value == NSNumber.
func performRule(value: NSNumber) -> Bool {
number.decimalValue // works
value.decimalValue // also works!
return true
}
}

Why this is working? Swift (UnsafeMutablePointer<Optional>)

protocol Property {}
protocol OptionalProtpery : Property {
static func codeNilInto(pointer: UnsafePointer<Int>)
}
extension Optional : OptionalProtpery {
static func codeNilInto(pointer: UnsafePointer<Int>) {
(UnsafeMutablePointer(pointer) as UnsafeMutablePointer<Optional>).memory = nil
}
}
I am interested in this statement UnsafeMutablePointer<"Optional">
Why we don't have an error for instance: "Optional generic type and requires arguments <...>". Since specific type (for instance UnsafeMutablePointer<"Optional"<"Int">>) is not specified right over here.
protocol Property {}
protocol OptionalProtpery : Property {
static func codeNilInto(pointer: UnsafePointer<Int>)
}
extension Optional : OptionalProtpery {
static func codeNilInto(pointer: UnsafePointer<Int>) {
(UnsafeMutablePointer(pointer) as UnsafeMutablePointer<Optional>).memory = nil
}
}
var i = 1
let p = withUnsafePointer(&i) { (p) -> UnsafePointer<Int> in
return p
}
print(p.memory) // 1
//Optional.codeNilInto(p) // error: generic parameter 'Wrapped' could not be inferred
// you have to specify generic parameter !!!
Optional<Int>.codeNilInto(p)
Optional<Double>.codeNilIntro(p)
// or what ever you want to express
so, because Optional is generic, you code is 'OK'. By the way, what do you expect your code does?

how to solve "Protocol ... can only be used as a generic constraint..."

Consider this fragment of code:
public protocol Evaluable {
typealias ReturnType
func eval(s: State) -> ReturnType
}
protocol AlgebraicExpr : Evaluable {
}
public struct Const : AlgebraicExpr {
var value: Int = 0
public func eval(s: State) -> Int {
return value
}
}
public struct Add : AlgebraicExpr {
let a, b: AlgebraicExpr
public func eval(s: State) -> Int {
return a.eval() + b.eval()
}
}
it is invalid because Add cannot have an AlgebraicExpr variable.
But I really need Add to store two AlgebraicExprs. How would you solve this in Swift?
For completeness, State is just a struct, for example:
public struct State {
var env = Dictionary<String, Int>()
init(_ d: Dictionary<String, Int> = [:]) {
env = d
}
subscript(s: String) -> Int? {
get {return env[s]}
set {env[s] = newValue}
}
}
You can't store two protocols that rely on Self, or a typealias because when abstracted as a protocol, the functionality is undetermined. Type Evaluable has no real meaning without understanding the value of ReturnType, so it's not possible for the system to function logically and understand what should be returned.
Take for instance something like this (not valid)
let thing: Evaluable = ...
let result = thing.eval(state) // What should return type be?
The return type can't be inferred through the protocol alone, it requires an implementation by a specified type that gives more knowledge about its behavior by defining the typealias ReturnType
The reason these can function as generic constraints is that the implementation can be inferred through the generic parameters.
func genericConstrained<T: Evaluable>(evaluable: T, state: State) -> T.ReturnType {
return evaluable.eval(state)
}
In this example, when we call genericConstrained on a particular conforming object, all of its other functionality can be inferred.
tl;dr
If however, you also constrain your package, you could do as you wish:
public struct Add<ExpressionType : AlgebraicExpr> : AlgebraicExpr {
let a, b: ExpressionType
public func eval(s: State) -> ExpressionType.ReturnType {
return a.eval() + b.eval()
}
}
Then use like
let add: Add<SomeConformingType> = ...