14

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.

Fattie
  • 27,874
  • 70
  • 431
  • 719
  • 2
    This isn't a job for generics, because there's nothing generic about using `String` or `Int` specifically Create an enumeration to store the various possibilities. – Alexander Dec 09 '16 at 15:29
  • The value you return isn't an issue, it's the matter of having a finite, static set of types you wish to constrain T to. – Alexander Dec 09 '16 at 15:35
  • 1
    Your math function shouldn't operate on multiple representations, that would be insane – Alexander Dec 09 '16 at 15:39
  • 1
    parse your input into `Int`, `Double`, or some other normal data type, process it that regular data, then generate the output in the correct format – Alexander Dec 09 '16 at 15:40
  • 1
    Hi @AlexanderMomchliov, in Swift3, is there syntax for generics, which means "the type T, must be either String or Int". Do you happen to know? – Fattie Dec 09 '16 at 15:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/130238/discussion-between-alexander-momchliov-and-joe-blow). – Alexander Dec 09 '16 at 15:50
  • As you know, SO comments are not a forum for extended discussion. I replied to your comment in chat. Reply there at your convince, and mention me (with @AlexanderMomchilov) so that i get notified – Alexander Dec 09 '16 at 16:19

4 Answers4

13

For this concrete case of an input is conceptually a "number" but it could be either a string in French, or, an integer, see Rickster's fantastic answer. In short, flatten all your various inputs into on cannonical form (of a single type), and work with that. In this example, that would mean parsing the French into an Int, and then only working with Int for your real computation.

For the more general case of needing to return one out of finite set of possible types, here's my original answer:

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

Alexander
  • 59,041
  • 12
  • 98
  • 151
  • A fantastic enumeration example, thanks. In Swift3, does anyone know is there syntax for generics, which means "the type T, must be either String or Int"? – Fattie Dec 09 '16 at 15:50
  • It could be a little roundabout, but could you use type constraints to enforce this? http://stackoverflow.com/questions/24089145/multiple-type-constraints-in-swift – Alex Dec 09 '16 at 16:30
  • @Alex Yes, but It's not as expressive as laying it out in an enumeration – Alexander Dec 09 '16 at 16:51
2

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 }
}
Alex
  • 3,861
  • 5
  • 28
  • 44
1

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.)

Community
  • 1
  • 1
rickster
  • 124,678
  • 26
  • 272
  • 326
  • HI @rickster, thanks for the extended answer. Really the "french-ints" example was just an example. A simple use example might be, return a "reasonable value" if an optional is null ("" for String, 0 for int, 0.0 for float .. whatever). – Fattie Dec 10 '16 at 00:08
-1

We can declare generics like below and also provided example usage please check it

 func genericTest<T>(_ a: inout T,  _ b: inout T)
{
 let c = a
  a = b
   b = c
  }
  var a = 10
       var b = 20
       genericTest(&b, &a)
       print("av:\(a)---\(b)")

       var c = "myTest"
       var d = "new Test"
       genericTest(&c, &d)
       print("cv:\(c)---\(d)")
Ru Chern Chong
  • 3,692
  • 13
  • 33
  • 43