Problem writing a generic function that accepts any value that is RawRepresentable by a CustomStringConvertible - swift

I'm trying to write a function that accepts any value that is RawRepresentable by a CustomStringConvertible. I tried writing this:
enum MyEnum: String {
case a = "someString"
}
func myFunction<R: RawRepresentable>(val: R) where R.RawValue == CustomStringConvertible {
print(val.rawValue.description)
}
myFunction(val: MyEnum.a)
However I get the following error:
Global function 'myFunction(val:)' requires the types 'String' and 'CustomStringConvertible' be equivalent
Which is weird, since String does conform to CustomStringConvertible.
Conforming the RawValue to just String works, however, I'd like to make this work with other CustomStringConvertible.
Why does this not compile, and is there a way I can achieve that?

You should say that it conforms the protocol
where R.RawValue: CustomStringConvertible
Now it works also for other types
enum MyEnum2: Int {
case one = 1
}
myFunction(val: MyEnum2.one)

Related

Make a function parameter be of a protocol type that also conforms to a protocol

I want to make a function that takes a parameter of a protocol type and ensure that that protocol type also conforms to another protocol type. Is there a way I can do this? Or do I have to fundamentally rethink my approach here?
Example:
// This doesn't extend CaseIterable itself because I would like to use it as a concrete type and not just a generic constraint
protocol MyProtocol {
/*some protocol stuff*/
}
enum myEnum: MyProtocol, CaseIterable {
/*some enum stuff*/
}
func<T: CaseIterable>(_ myEnum: MyProtocol.Type)
where MyProtocol.Type: CaseIterable {
myEnum.allCases // <--- This is what I would like to do
}
You need to change your function signature to use the generic type constraint as the input argument's type and restrict the generic type parameter to be MyProtocol and CaseIterable.
func enumFunc<T: MyProtocol & CaseIterable>(_ myEnum: T.Type) {
myEnum.allCases // <--- This is what I would like to do
}

Can you Strict Generic types or give one parameter more than one type ?

For example i want to specify a type that might be Integer or String and use it as special type in func i tried typealias
but it wont solve the case because typealiases can't have or arguments as its only uses & therefore consider the case below.
typealias alis = StringProtocol & Numeric
func foo <T: alis> (vee: T) -> T{
// do something
return vee
}
i want this func to accept a parameter type of either ( Int or String ) not anything else (<T>),
as you can see i tried with typealias and i have no compile error.
however trying to use the function will lead to these errors.
foo(vee: 1) //Argument type 'Int' does not conform to expected type 'StringProtocol'
And
foo(vee: "v") //Argument type 'String' does not conform to expected type 'Numeric'
is this achievable with swift ? if so how.
Let's suppose that you could use a OR operator to combine protocols, what would you be able to do with something of type (Int | String)?
Not everything you can do to an Int can be done on (Int | String), because it might be a string underlyingly. Similarly, not everything you can do to an String can be done on (Int | String), because it might be a Int underlyingly.
Now you might say "Ah. I know that Int and String both have a description property. I should be able to access description on a variable of type (Int | String)."
Well, in that case, you can just create such a protocol yourself and only have Int and String conform to it:
protocol IntOrString {
var description: String { get }
}
extension Int : IntOrString {}
extension String : IntOrString {}
(Note that description is already defined in CustomStringConvertible. For the sake of argument, imagine that does not exist.)

Generic constraints specific to an enum member

I have a protocol with an associated type:
protocol MyProtocol {
associatedtype Q
}
Now I'd like to have an enum like
enum MyEnum<Q> {
case zero
case one(MyProtocol)
case two(MyProtocol, MyProtocol)
}
where each associated value has Q as its associated type. This doesn't work:
enum MyEnum<Q> {
case zero
case one<P: MyProtocol where P.Q == Q>(P)
case two<P1: MyProtocol, P2: MyProtocol where P1.Q == Q, P2.Q == Q>(P1, P2)
}
Apparently, individual enum members can't have their own generic constraints.
The only thing I can think of is to move those constraints to the enum declaration, but this fixates the associated types. To demonstrate why that's not what I want, this is what I'd like to be able to do:
struct StructA: MyProtocol {
typealias Q = Int
}
struct StructB: MyProtocol {
typealias Q = Int
}
var enumValue = MyEnum.one(StructA())
enumValue = .two(StructB(), StructA())
enumValue = .two(StructA(), StructB())
Is there a way around this limitation?
Type erasure. The answer is always type erasure.
What you need is an AnyProtocol type:
struct AnyProtocol<Element>: MyProtocol {
typealias Q = Element
// and the rest of the type-erasure forwarding, based on actual protocol
}
Now you can create an enum that uses them
enum MyEnum<Q> {
case zero
case one(AnyProtocol<Q>)
case two(AnyProtocol<Q>, AnyProtocol<Q>)
}
For a deeper discussion of how to build the type erasers see A Little Respect for AnySequence.
Swift cannot discuss PATs (protocols with associated types) as real types or even abstract types. They can only be constraints. In order to use it as even an abstract type, you have to distill it into a type eraser. Luckily this is quite mechanical and in most cases not difficult. It's so mechanical that eventually the compiler will hopefully do the work for you. But someone has to build the box, and today that's you.

Swift "Enum" base object

If I want to add an extension to every object I can do the following:
extension AnyObject
{
func myFunc() { ... }
}
Is there something similar where I can add a function to every Enum? In other words, what is the base "class" for every enum?
First of all, note that you cannot do an extension to AnyObject as above, as AnyObject is a protected protocol (to which all classes implicitly conform) rather than a type. See e.g.
Is there a way to add an extension to AnyObject?
Now, you could, however, extend any specific type as you show above, e.g., extension Int { ... }. However enum is not a type; rather a "container" of ordered raw representable values. So a more valid comparison could be "If I want to add an extension to every class, by extension class ...", which is, naturally, trickier.
Now, all enumerations with a rawValue and an initializer by rawValue conforms to protocol RawRepresentable, so you could extend this protocol for specific types of raw values:
extension RawRepresentable where RawValue == Int {
var sqrt: Double {
return pow(Double(rawValue),(1/2))
}
}
And this extension property would be available to all enumerations that explicitly use the same raw value type, in this case, Int:
enum MyIntegerLiteralEnum : Int {
case One = 1
case Two
case Three
case Four
}
print(MyIntegerLiteralEnum.One.sqrt)
print(MyIntegerLiteralEnum.Two.sqrt)
print(MyIntegerLiteralEnum.Four.sqrt)
/* 1.0
1.4142135623731
2.0 */
As a disclaimer, note that this extension will be made available to all types that conforms to RawRepresentable with a rawValue of type Int, not only enum types. E.g.:
struct Foo : RawRepresentable {
typealias RawValue = Int
var bar : Int
var rawValue: RawValue {
get {
return bar
}
}
init(rawValue bar: Int) {
self.bar = bar
}
}
var a = Foo(rawValue: 16)
print(a.sqrt) // 4.0

Is it possible to declare swift generic for enums of particular type?

Would it be possible to have a function that allows any enum where the rawValue is of a certain type? For example, any enum that has a string rawValue.
This can be done using generics and the "where" keyword
enum EnumString: String {
case A = "test"
}
func printEnum<T: RawRepresentable where T.RawValue == String>(arg: T) {
print(arg.rawValue)
}
printEnum(EnumString.A) //Prints "test"
You can declare a generic that conforms to the type RawRepresentable, which is a protocol that all enums declaring a primitive rawValue conform to.
enum EnumA: Int {
case A = 0
}
enum EnumB {
case A
}
func doGenericSomething<T: RawRepresentable>(arg: T) {
println(arg.rawValue)
}
doGenericSomething(EnumA.A) //OK
doGenericSomething(EnumB.A) //Error! Does not conform to protocol RawRepresentable
You cannot, however, specify an enum's rawValue type in a generic. For information you can see the post here.