Recursive Enums and String Literals


(Rick Gigger) #1

I am trying to create an enum that will allow me to create literals that
combine Int, Double and String types, as well as any Array of any
combination of those, and any Dictionary mapping a String to any
combination of those. And make it nestable.

First I have the enum itself, and then several examples. Everything
complies cleanly except for dict4 and dict5.

Is type inference not expected to work for recursive enums? Or is this a
bug in the compiler? If not is there any way to do this in pure swift that
will compile?

I realize that it's sort of unswifty to have such unstructured, stringly
typed data like this. But in a pinch sometimes it's useful.

Copy and paste this into a Playground to see the error message. (Contextual
type 'protocol <>' cannot be used with array literal and Contextual type
'protocol <>' cannot be used with dictionary literal)

enum RecursiveAny: StringLiteralConvertible, ArrayLiteralConvertible,
IntegerLiteralConvertible, BooleanLiteralConvertible,
FloatLiteralConvertible, DictionaryLiteralConvertible {

    case any(Any)

    indirect case anyDict([String:Any])

    indirect case anyArr([Any])

    // string literal convertible

    init(stringLiteral value: String) {

        self = .any(value)

    }

    init(extendedGraphemeClusterLiteral value: String) {

        self = .any(value)

    }

    init(unicodeScalarLiteral value: String) {

        self = .any(value)

    }

    // array literal convertible

    init(arrayLiteral elements: Any...) {

        self = .anyArr(elements)

    }

    init(dictionaryLiteral elements: (String, Any)...) {

        var dict = [String:Any]()

        for (key, value) in elements {

            dict[key] = value

        }

        self = .anyDict(dict)

    }

    // integer literal convertible

    init(integerLiteral value: Int) {

        self = .any(value)

    }

    // boolean literal convertible

    init(booleanLiteral value: Bool) {

        self = .any(value)

    }

    // float literal convertible

    init(floatLiteral value: Double) {

        self = .any(value)

    }

}

let string: RecursiveAny = "asdf"

let int: RecursiveAny = 3

let float: RecursiveAny = 5.6

let array: RecursiveAny = ["asdf", 3, 5.6]

let dict: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6

]

let dict2: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "array": array

]

let dict3: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "array": dict

]

let dict4: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "array": ["asdf", 3, 5.6]

]

let dict5: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "dict": [

        "string": "asdf",

        "int": 3,

        "float": 5.6

    ]

]

let dict6: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "array": RecursiveAny.anyArr(["asdf", 3, 5.6])

]

let dict7: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "dict": RecursiveAny.anyDict([

        "string": "asdf",

        "int": 3,

        "float": 5.6

    ])

]


(Rick Gigger) #2

I just realized that two of the cases in the enum were wrong. They should
have been:

    indirect case anyDict([String:RecursiveAny])
    indirect case anyArr([RecursiveAny])

That change forced me to update some of the enum methods, but it
didn't end up changing the compile status of any of the tests below
it.

···

On Sun, Dec 27, 2015 at 3:14 AM Rick Gigger <swift@rickgigger.com> wrote:

I am trying to create an enum that will allow me to create literals that
combine Int, Double and String types, as well as any Array of any
combination of those, and any Dictionary mapping a String to any
combination of those. And make it nestable.

First I have the enum itself, and then several examples. Everything
complies cleanly except for dict4 and dict5.

Is type inference not expected to work for recursive enums? Or is this a
bug in the compiler? If not is there any way to do this in pure swift that
will compile?

I realize that it's sort of unswifty to have such unstructured, stringly
typed data like this. But in a pinch sometimes it's useful.

Copy and paste this into a Playground to see the error message.
(Contextual type 'protocol <>' cannot be used with array literal and
Contextual type 'protocol <>' cannot be used with dictionary literal)

enum RecursiveAny: StringLiteralConvertible, ArrayLiteralConvertible,
IntegerLiteralConvertible, BooleanLiteralConvertible,
FloatLiteralConvertible, DictionaryLiteralConvertible {

    case any(Any)

    indirect case anyDict([String:Any])

    indirect case anyArr([Any])

    // string literal convertible

    init(stringLiteral value: String) {

        self = .any(value)

    }

    init(extendedGraphemeClusterLiteral value: String) {

        self = .any(value)

    }

    init(unicodeScalarLiteral value: String) {

        self = .any(value)

    }

    // array literal convertible

    init(arrayLiteral elements: Any...) {

        self = .anyArr(elements)

    }

    init(dictionaryLiteral elements: (String, Any)...) {

        var dict = [String:Any]()

        for (key, value) in elements {

            dict[key] = value

        }

        self = .anyDict(dict)

    }

    // integer literal convertible

    init(integerLiteral value: Int) {

        self = .any(value)

    }

    // boolean literal convertible

    init(booleanLiteral value: Bool) {

        self = .any(value)

    }

    // float literal convertible

    init(floatLiteral value: Double) {

        self = .any(value)

    }

}

let string: RecursiveAny = "asdf"

let int: RecursiveAny = 3

let float: RecursiveAny = 5.6

let array: RecursiveAny = ["asdf", 3, 5.6]

let dict: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6

]

let dict2: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "array": array

]

let dict3: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "array": dict

]

let dict4: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "array": ["asdf", 3, 5.6]

]

let dict5: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "dict": [

        "string": "asdf",

        "int": 3,

        "float": 5.6

    ]

]

let dict6: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "array": RecursiveAny.anyArr(["asdf", 3, 5.6])

]

let dict7: RecursiveAny = [

    "string": "asdf",

    "int": 3,

    "float": 5.6,

    "dict": RecursiveAny.anyDict([

        "string": "asdf",

        "int": 3,

        "float": 5.6

    ])

]


(Joe Groff) #3

I am trying to create an enum that will allow me to create literals that combine Int, Double and String types, as well as any Array of any combination of those, and any Dictionary mapping a String to any combination of those. And make it nestable.

First I have the enum itself, and then several examples. Everything complies cleanly except for dict4 and dict5.

Is type inference not expected to work for recursive enums? Or is this a bug in the compiler? If not is there any way to do this in pure swift that will compile?

I realize that it's sort of unswifty to have such unstructured, stringly typed data like this. But in a pinch sometimes it's useful.

Copy and paste this into a Playground to see the error message. (Contextual type 'protocol <>' cannot be used with array literal and Contextual type 'protocol <>' cannot be used with dictionary literal)

`Any` is a typealias for the `protocol<>` type, so the type checker's complaining that it's unable to find a suitable Array/DictionaryLiteralConvertible type. This is arguably a bug, since the type checker's supposed to fall back to Array/Dictionary as defaults in unconstrained cases like this, but I think it's helping you out here, since you really want to build your data structure out of RecursiveAny rather than unconstrained Any values. If you replace `Any` with `RecursiveAny` in your anyDict/anyArr payload types, it should work.

-Joe

···

On Dec 27, 2015, at 2:14 AM, Rick Gigger via swift-users <swift-users@swift.org> wrote:

enum RecursiveAny: StringLiteralConvertible, ArrayLiteralConvertible, IntegerLiteralConvertible, BooleanLiteralConvertible, FloatLiteralConvertible, DictionaryLiteralConvertible {
    case any(Any)
    indirect case anyDict([String:Any])
    indirect case anyArr([Any])
    
    // string literal convertible
    init(stringLiteral value: String) {
        self = .any(value)
    }
    
    init(extendedGraphemeClusterLiteral value: String) {
        self = .any(value)
    }
    
    init(unicodeScalarLiteral value: String) {
        self = .any(value)
    }
    
    // array literal convertible
    init(arrayLiteral elements: Any...) {
        self = .anyArr(elements)
    }
    
    init(dictionaryLiteral elements: (String, Any)...) {
        var dict = [String:Any]()
        for (key, value) in elements {
            dict[key] = value
        }
        self = .anyDict(dict)
    }
    
    // integer literal convertible
    init(integerLiteral value: Int) {
        self = .any(value)
    }
    
    // boolean literal convertible
    init(booleanLiteral value: Bool) {
        self = .any(value)
    }
    
    // float literal convertible
    init(floatLiteral value: Double) {
        self = .any(value)
    }
}

let string: RecursiveAny = "asdf"
let int: RecursiveAny = 3
let float: RecursiveAny = 5.6
let array: RecursiveAny = ["asdf", 3, 5.6]
let dict: RecursiveAny = [
    "string": "asdf",
    "int": 3,
    "float": 5.6
]
let dict2: RecursiveAny = [
    "string": "asdf",
    "int": 3,
    "float": 5.6,
    "array": array
]
let dict3: RecursiveAny = [
    "string": "asdf",
    "int": 3,
    "float": 5.6,
    "array": dict
]
let dict4: RecursiveAny = [
    "string": "asdf",
    "int": 3,
    "float": 5.6,
    "array": ["asdf", 3, 5.6]
]
let dict5: RecursiveAny = [
    "string": "asdf",
    "int": 3,
    "float": 5.6,
    "dict": [
        "string": "asdf",
        "int": 3,
        "float": 5.6
    ]
]

let dict6: RecursiveAny = [
    "string": "asdf",
    "int": 3,
    "float": 5.6,
    "array": RecursiveAny.anyArr(["asdf", 3, 5.6])
]
let dict7: RecursiveAny = [
    "string": "asdf",
    "int": 3,
    "float": 5.6,
    "dict": RecursiveAny.anyDict([
        "string": "asdf",
        "int": 3,
        "float": 5.6
    ])
]

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users