Is it posisble to unbox existential value?

Hello. Recently I have encountered an interesting case: I wanted to implement a function type to bring powerful abilities of inheritance to functions. I have stuck with this code.

protocol TypedEntity {   //Look! No Selfs!
    var type: Any.Type { get }
    var _self: Any { get }    
}
typealias Arguments = Dictionary<String, TypedEntity>
class Function<Arguments, R> { ... }
//if coercion can be dealt, something like this will be possible
class CachingFunction<Arguments, R>: Function<Arguments, R> { ... }
let someFunc = Function { ... }
someFunc.precondition { ... }
someFunc <<! ["arg1": Type, "arg2": Type]
//etc i hope you get the idea

So what I wanted to achieve is a way to cast TypedEntity to its concrete type and then perform some computations, but having tried almost all options : unsafeBitCast, as operators, type(of:) function I failed to find the way. If I assume correctly, this thing is impposible in swift as of now due to some minor limitations in cast behavior. I think swift should have runtime casting allowed in some way, because benefit is huge. What do you think about it?

//something like this would be great imo
extension TypedEntity {
    func unbox<T>() -> T {
        return self._self as! self.type
    }
}
type(of: "") is String //is possible
0 as type(of: "") //but not this?

self._self as! self.type does not make much sense as self.type already is of type Any.Type, which means that the type you store is already boxed and cannot be extracted in a way you want to.

However here is a nice technique used in the sodlib to implement AnyHashable, maybe this will suit your use case: swift/AnyHashable.swift at main · apple/swift · GitHub

When you invoke a method on an existential, the value is automatically unboxed, and the dynamic type in the value becomes the Self type inside methods on the protocol or its extensions. You can then use Self as a first-class type in casts or as the type of other variables inside those methods.

Oh my god :open_mouth:, thank you, Joe. This works!

protocol TypedEntity {   //Look! No Selfs!
    var type: Any.Type { get }
    var _self: Any { get }    
}
extension TypedEntity {
    func unbox<T>() -> T {
        return self._self as! Self as! T
    }
}

Now the only thing I need is to make an automatic synthesis so all used types in a programm to be conformed to TypedEntity protocol :woozy_face:. Is there anything better than inserting autogenerated raw text into source code?

extension String: TypedEntity {
    var type: Any.Type { return String.self }
    var _self: Any { return self }
}
typealias TrueAny = TypedEntity
let someVal: TrueAny = "!"
print(
    someVal, 
    someVal.unbox(), 
    someVal.type,
    someVal.type is String, //Wat?! How is this false :\
    type(of: someVal.type),
    type(of: someVal)
)

Now this brings some questions about Any being a supertype for both structurals and nomninals, because if there were a dufference, Any could be easily extended. Should a proposal be addressed at this problem? Maybe something like Any -> [AnyNominal, AnyStructural] -> ... Also this point out that tuples should be actually bounded arrays.

What do you want the TypedEntity protocol to do? It looks pretty much like exactly what Any already is.

I could use Any, but if I do so then types become erased, if i am not mistaken, and there is no way to rebound it. I want to use it to make some generic containers.

The type inside Any is not really erased; it's represented as effectively a tuple of the dynamic type of the value and the value itself. If you expect a specific type, you can get it back out with any as! T just like you're doing with your TypedEntity.

Oh, i think i screwed up :slightly_smiling_face:.
I wanted to achieve the following

let iAmGeneric = Function { (arg: TypedEntity, arg2: TypedEntity) -> Void in
    let val1 = arg.unbox(); let val2 = arg2.unbox()
    print(val1 + val2)
}
//another one
let container = Resource { "this is just string" }
container.constraint { $0 is CustomStringConvertible }
container =! "replacement"

But it doesn't compile, since it does not actually unboxes to its concrete type. Hell.

You can't implement unbox() without knowing what type to unbox to.

If you want a common type for Function that can take dynamically-typed values, I think it would work better to handle the type erasure on construction of Function. Something like this could work:

struct Function {
  var fn: (Any, Any) -> Void

  init<First, Second>(fn: (First, Second) -> Void)  {
    self.fn = { fn($0 as! First, $1 as! Second) }
  }
}

let f = Function {(a: Int, b: Int) -> Void in print(a + b) }

f.fn(1,  2) // prints 3
f.fn("one", 2) // type mismatch, fails at runtime
1 Like