Skip to content

Instantly share code, notes, and snippets.

@chris-hatton
Created September 9, 2016 02:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chris-hatton/d4e1c3726241cfaa890a1efd5409d03f to your computer and use it in GitHub Desktop.
Save chris-hatton/d4e1c3726241cfaa890a1efd5409d03f to your computer and use it in GitHub Desktop.
Associated types and Generic constraints not playing nice in Swift 3.0
import UIKit
protocol HTTPRequest
{
associatedtype ResultType
func start( callback: (ResultType)->() )
}
class ObjectRequest<Result> : HTTPRequest
{
typealias ResultType = Result
func start( callback: (ResultType)->() )
{
print("Pretending to start a request for \(ResultType.self)!")
callback( OAuthToken() as! ResultType ) // Obviously a fragile cast, we only need ObjectRequest<OAuthToken> for purposes of this demo.
}
}
protocol Authentication
{
associatedtype SessionCredentials : CustomStringConvertible
associatedtype LoginRequestType : HTTPRequest
// The crux of the issue that I am reporting is here and again on line 50; I should be able to constrain createLoginRequest to produce only a request
// whose ResultType is SessionCredentials, but this is unexpectedly rejected by the compiler (line 3). More than just a language design gripe, the second
// line actually causes the compiler to crash, which leaves me wondering if what I am attempting *should* work but is just plain buggy.
func createLoginRequest() -> LoginRequestType // 1. Works but needs cast below
//func createLoginRequest<R>() -> R where R == LoginRequestType // 2. Should be equivalent to (1) but crashes compiler
//func createLoginRequest<R>() -> R where R == LoginRequestType, R.ResultType == SessionCredentials // 3. Should be valid and remove need to force cast below but doesn't compile
}
class OAuthToken : CustomStringConvertible
{
var description: String { return "ABC123" }
}
class OAuth : Authentication
{
typealias SessionCredentials = OAuthToken
typealias LoginRequestType = ObjectRequest<SessionCredentials>
func createLoginRequest() -> LoginRequestType // 1. Works but needs cast below
//func createLoginRequest<R>() -> R where R : LoginRequestType // 2. Should be equivalent to (1) but crashes compiler
//func createLoginRequest<R>() -> R where R : LoginRequestType, R.ResultType : SessionCredentials // 3. Should be valid and remove need to force cast below but doesn't compile
{
return ObjectRequest<OAuthToken>() // as! R
}
}
final class Session<AuthType:Authentication>
{
let authentication : AuthType
init( authentication: AuthType )
{
self.authentication = authentication
}
func login()
{
let loginRequest = authentication.createLoginRequest() as AuthType.LoginRequestType
loginRequest.start
{
(result: AuthType.LoginRequestType.ResultType) in
let credentials = result as! AuthType.SessionCredentials // Using line (1) above requires use of a force-cast here, weakening the design, which should be avoidable with proper constraints (3)
print("Pretending to receive credentials \(credentials.description)")
}
}
}
let session = Session( authentication: OAuth() )
session.login()
@chris-hatton
Copy link
Author

chris-hatton commented Sep 9, 2016

I've filed a bug report with Apple on this one because using line (2) causes the Swift compiler to crash. Furthermore, I believe that the constraints in commented line (3) should parse, and don't. Nor do any variants; it seems to be impossible to constrain createLoginRequest()to only produce Requests which have SessionCredentials as their result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment