[swift-evolution] [Discussion] access control modifier inconsistency

Adrian Zubarev adrian.zubarev at devandartist.com
Mon Jun 20 04:15:56 CDT 2016


Access control modifier: public > internal == /* implicitly internal */ > fileprivate >= private

//===== TOPLEVEL / File - Scope

//===== some protocols  

public protocol A {
     
    func aa()  
}

private protocol B {
     
    func bb()  
}

fileprivate protocol C {
     
    func cc()  
}

internal protocol D {
     
    func dd()  
}

/* implicitly internal */ protocol E {
     
    func ee()  
}

//===== some types

public struct FF {}

private struct GG {}

fileprivate struct HH {}

internal struct II {}

/* implicitly internal */ struct JJ {}

//===== some extensions

// We should be able to extend it with all protocols: A, B, C, D, E
// BUT it depends whether all protocols are visible at this scope
// In this example they are visible!
//
// The compiler should force you to write modifier for: A, B and C
// `internal` is always optional

public extension FF: A, B, C, D, E {

    public func aa()
     
    // Visible in this scope only
    private func bb()
     
    // Visible in this file
    fileprivate func cc()
     
    // Visible for the whole module
    // We do not need to write out `internal` here
    /* implicitly internal */ func dd()
     
    // visible for the whole module
    /* implicitly internal */ func ee()
}

// At top-level `private` is like `fileprivate`
private extension FF: A, B, C, D, E {  
     
    // Same as in `public extension`
    // Except everything is `private` (or `fileprivate`)
}

fileprivate extension FF: A, B, C, D, E {  
     
    // Same as in `public extension`
    // Except everything is `fileprivate`
}

internal extension FF: A, B, C, D, E {  
     
    // Same as in `public extension`
    // Except everything is `internal` or explicitly `private` or `fileprivate`
}

/* implicitly internal */ extension FF: A, B, C, D, E {  
     
    // Same as in `internal extension`
}

// For GG, HH, II and JJ we cannot extend these types with a `public` modifier!
// GG and HH can't be extended with `internal` modifier
If I missed anything, please let me know.

Protocols should not import an access modifier on their members, because this is a documented rule that we use the modifier from protocols definition.

public protocol A {
    var someMember: Type {}
}

// SHOULD NOT LOOK LIKE THIS:
public protocol A {
    public var someMember: Type {}
}

// CORRECT VERSION:
public protocol A {
 var someMember: Type {}
}


-- 
Adrian Zubarev
Sent with Airmail

Am 20. Juni 2016 um 10:35:59, Adrian Zubarev (adrian.zubarev at devandartist.com) schrieb:

Everyone does feel comfortable with implicit public extensions?



-- 
Adrian Zubarev
Sent with Airmail

Am 19. Juni 2016 um 09:18:35, Adrian Zubarev (adrian.zubarev at devandartist.com) schrieb:

One problem I found with the mentioned suggestion of mine is this:

public protocol A {}

public class B {}

// 'public' modifier cannot be used with    
// extensions that declare protocol conformances
public extension B: A {}
Why is that like this?
Can this be fixed?
Implicit public extension feels odd and is inconsistent if you ask me.
Any statement to the pitched suggestion?



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Juni 2016 um 11:28:18, Adrian Zubarev (adrian.zubarev at devandartist.com) schrieb:

I’ve spotted some inconsistency on access control modifier I’d like to discuss in this thread (the behavior is not groundbreaking but inconsistent from readers and developers view perspective).

Why are extensions not marked as public in public apis when the module is imported?

Any type members added in an extension have the same default access level as type members declared in the original type being extended. If you extend a public or internal type, any new type members you add will have a default access level of internal. If you extend a private type, any new type members you add will have a default access level of private.

Alternatively, you can mark an extension with an explicit access level modifier (for example, private extension) to set a new default access level for all members defined within the extension. This new default can still be overridden within the extension for individual type members.

Source: Apple Inc. The Swift Programming Language (Swift 2.2).
This does not tell us how the access control modifier works on extensions. Here are three examples:
public struct A {}
        
/* implicitly internal */ extension A {}
        
// This extension won't be exported
as soon as at least one member modifier of an extension is public (if the extended type allows that and is also public), the extension itself becomes implicitly public:
public struct B {}
        
/* implicitly public */ extension B {
            
    public func foo() {}        
            
    /* implicitly internal */ func boo() {}
}
        
// This extension will be exported as
        
extension B {
            
    public func foo()
}
This is inconsistent to other types, because you can not leave out the modifier for instance on classes, then add a public member and assume that your class is implicitly public! The compiler knows that and provides a correct warning that the mentioned class is internal.

public struct C {}
        
public extension C {
            
    public func foo() {}        
            
    /* implicitly internal */ func boo() {}
}
        
// This extension will be exported as
        
/* public is missing here */ extension C {
            
    public func foo()
}
Extensions seem to behave differently and its behaviors is inconsistent and not documented.

protocols has explicit public modifier on its members when the module is imported. However, 'public' modifier cannot be used in protocols is how the compiler treat modifiers in protocols on declaration.

public protocol D {
            
    func doSomething()
}
Protocol D will be exported as:

public protocol D {
            
    public func doSomething()
}
Here is my suggestion:

Fix the behavior for extensions and document it correctly:

public struct B {}
        
/* implicitly public */ extension B {
            
    public func foo() {}        
            
    /* implicitly internal */ func boo() {}
}
Such an extension should not be implicitly public and behave like other types and stay implicitly internal.
The compiler should provide a warning for the foo() function: Declaring a public function for an internal type
Extensions should have explicit public modifier like other types if the extension was marked as public and was imported into an other project/module.
This inconsistent behavior is source breaking and I’d suggest this change to happen in Swift 3!
Remove public modifier from imported protocols OR document this behavior correctly!



-- 
Adrian Zubarev
Sent with Airmail
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160620/98808371/attachment.html>


More information about the swift-evolution mailing list