[swift-users] still wrestling with a type conversion problem

Jacob Bandes-Storch jtbandes at gmail.com
Tue Jul 4 00:58:29 CDT 2017


Using a protocol with an "as?" cast convinces the compiler to do a dynamic
lookup for a protocol conformance based on the value's actual type:

    protocol MyEncodable {
        func encoded() -> Any
    }

    func encode<T>(_ value: T) -> Any {
        if let value = value as? MyEncodable {
            // print("Calling complex encoding for value")
            return value.encoded()
        } else {
            // print("Calling simple encoding for value")
            return value
        }
    }

    extension Set: MyEncodable {
        func encoded() -> Any {
            // print("Calling complex encoding for Set")
            return self.map { $0 }
        }
    }


    print(type(of: encode(3)))                      // Int
    print(type(of: encode(Set([3,4,5]))))          // Array<Int>

    func genericEncode<T>(_ value:T) -> Any {
        return encode(value)
    }

    print(type(of: genericEncode(3)))               // Int
    print(type(of: genericEncode( Set([3,4,5]))))   // *Array<Int>*


(You could've just used a protocol for everything, and done away with
encode() as a free function, except that you can't make .encoded() work on
any arbitrary type without an explicit conformance, and "extension Any:
MyEncodable" isn't allowed.)

-Jacob

On Mon, Jul 3, 2017 at 10:44 PM, David Baraff via swift-users <
swift-users at swift.org> wrote:

> I’m trying to provide some custom serialization from within a generic
> class.
> (Briefly put, I want to automatically convert types like Set to an array
> upon serialization, and the reverse when I read it back.)
>
> While trying things out, I was suprised by this:
>
> public func encode<T>(_ value:T) -> Any {
>     // print("Calling simple encoding for value")
>     return value
> }
>
> public func encode<T>(_ value:Set<T>) -> Any {
>     // print("Calling complex encoding for value")
>     return value.map { $0 }
> }
>
> ——————————————
>
> The above transforms a Set to a list, but leaves everything else alone.
> As expected:
>
>
> print(type(of: encode(3)))                      // Int
> print(type(of: encode( Set([3,4,5]))))          // Array<Int>
>
> Good.  But now I do this:
>
> func genericEncode<T>(_ value:T) -> Any {
>     return encode(value)
> }
>
> print(type(of: genericEncode(3)))               // Int
> print(type(of: genericEncode( Set([3,4,5]))))   // Set<Int>
>
> Aha.  Inside my generic function, I’ve “lost” the ability to overload
> based on type.  In retrospect, this is perhaps not to surprising.  Still,
> is there anyway that I can provide specialized versions of my functions
> which will take effect when called from *within* a generic function?
>
> I’m basically trying to leave any type that is happy to go to/from
> UserDefaults alone, but for some set of types which isn’t JSON serializable
> (e.g. Set) I’d like to provide a simple pathway.  I don’t aim to hit every
> type in the world, but right now, I’m *totally* stymied when trying to do
> the serialization from within a generic class, as I lost any ability to
> specialize which function gets called.
>
> [I’m from the C++ world of templates and metaprogramming, and I’m shifting
> from one set of constraints to another.  The swift type system with
> generics is cool and powerful, but man i’m still trying to really wrap my
> head around it.  Not that C++ was any easier…]
>
>
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170703/54efdb62/attachment.html>


More information about the swift-users mailing list