Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Swift can you constrain a generic to two types? String and Int

In Swift3, can you do this ...

 func example<T>()->(T) where T can only be String or Int

Or, do you just have to have two extensions?

For example, the function calculates a math function.

The input is conceptually a "number" but it could be either a string in French, or, an integer. So the input could be a String "cent" or an Int 100. the result would be say the square root, so either String "dix" or Int 10.

like image 807
Fattie Avatar asked Dec 09 '16 15:12

Fattie


3 Answers

This is not a job for generics. There's nothing generic about working with a static set of two types.

This the perfect situation to use an enumeration:

enum SomeEnum { //TODO: name me
    case string(String)
    case int(Int)
}

func foo(input: SomeEnum) -> SomeEnum {
    switch input {
        case .string(let s):
            print("This is a String: \(s)")
            return(.string("abc"))
        case .int(let i):
            print("This is an Int: \(i)")
            return(.int(123))
    }
}

print(foo(input: SomeEnum.string("qwerty")))
print(foo(input: SomeEnum.int(5)))

You can try it here

like image 141
Alexander Avatar answered Dec 01 '22 20:12

Alexander


I was thinking something like this, although it does seem that an enumeration would be a fine answer.

protocol HasSquareRootMethod {
    var squareRoot: Int { get }
}
extension Int {
    var squareRoot: Int { return 0 }
}
extension String {
    var squareRoot: Int { return 0 }
}
like image 36
Alex Avatar answered Dec 01 '22 21:12

Alex


Other answers have demonstrated how to, in the general case, create a generic function constrained to two or more otherwise-unrelated types. If that general issue is what your question is really about, you're good to go.

However, if the example use case you provided resembles something you actually want to do, fooling around with generics probably isn't what you want.

The input is conceptually a "number" but it could be either a string in French, or, an integer. So the input could be a String "cent" or an Int 100. the result would be say the square root, so either String "dix" or Int 10.

This means you actually have (at least) two problems (and you didn't even use regular expressions!)...

  1. Convert user input text to a numeric type.

    (Probably your eventual type is Float or Double, because transcendental functions like square-root aren't defined on Int in the standard library. Also note that you have to do this even if user input is "numeric" -- that is, a string containing numerals -- because that string can contain other characters which may or may not affect its "meaning" as a number.)

  2. Find the square root of the number.

  3. (Optional, may or may not be part of your full design.) Present the answer in nice, human-readable format, possibly one resembling the format of the user's input.

Step 2 is simple, once you have a number. For Steps 1 and 3, you can at least get started with a NumberFormatter:

guard let num = formatter.number(from: "one hundred") as? Double
    else { fatalError("better error handling plz") }

let root = sqrt(num)

guard let rootText = formatter.string(from: root as NSNumber)
    else { fatalError("better error handling plz") }

print(rootText) // "ten"

Set formatter.locale = Locale(identifier: "ja_JP") and you get "十" for "百", use fr_FR or fr_CA and "cent" becomes "dix", etc.

However, interpreting user text is always messy. What if they enter "one hundred twenty one" instead of "one hundred twenty-one"? What about "eleventyone"? What if there are other extraneous characters? NumbeFormatter can't recognize the entire universe of possible natural-language numeric expressions.

You have some logic to think about in your processing and output, too. What's to happen if the user-entered number doesn't have an integer square root? Do you really want to display "ten point zero four nine eight seven five six two one one two zero nine" in your UI?

Big, hairy questions like this are a good reason to factor apart the different tasks in your program instead of running all your logic through one generic function. (And there are simple reasons, too, like being able to reuse your number/text conversion code for multiple math operations.)

like image 21
rickster Avatar answered Dec 01 '22 20:12

rickster