Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Figure out naming scheme in icrate #284

Open
madsmtm opened this issue Nov 4, 2022 · 10 comments
Open

Figure out naming scheme in icrate #284

madsmtm opened this issue Nov 4, 2022 · 10 comments
Labels
A-framework Affects the framework crates and the translator for them documentation Improvements or additions to documentation question Further information is requested
Milestone

Comments

@madsmtm
Copy link
Owner

madsmtm commented Nov 4, 2022

icrate (or whatever it'll end up being named) is nearing an initial version, see #264.

After that, we need to figure out how we should handle name translation for enums, method names, and so on. There are a few options:

  1. Use Objective-C naming scheme:
  • Framework: Foundation
  • Class: struct NSObject
  • Protocol: struct NSCopying
  • Category: trait NSAccessibility; impl NSAccessibility for NSObject; (or perhaps NSObject_NSAccessibility?)
  • Method: fn someMethod(arg: i32), fn someMethod_withExtraParam(arg: i32, extra: u32)
  • Method returning error: fn aMethod_error(arg: i32) -> Result<(), Id<NSError>>
  • Method with callback: async fn aMethodWithCompletionHandler(f: &Block<(), ()>), see also async support #279
  • Enum: struct NSAccessibilityAnnotationPosition(NSInteger)
  • Enum constant: const NSAccessibilityAnnotationPositionFullRange: NSAccessibilityAnnotationPosition = ...
  • Struct: struct NSDirectionalEdgeInsets { fields* }
  • Constant: const NSModalResponseStop: NSModalResponse = 1000
  • Typedef: struct NSBitmapImageRepPropertyKey(NSString)
  • Static: extern "C" { static NSImageCurrentFrameDuration: NSBitmapImageRepPropertyKey }
  • Function: extern "C" { fn NSBitsPerPixelFromDepth(depth: NSWindowDepth) -> NSInteger }
  1. Use a scheme similar to Swift's
  • Framework: Foundation
  • Class: Same, sometimes renamed?
  • Protocol: Same
  • Category: Same
  • Method: fn some(method: i32), fn some_with(method: i32, extraParam: u32) (name is significantly stripped)
  • Method returning error: fn a(arg: i32) -> Result<(), Id<NSError>> (error postfix is removed)
  • Method with callback: async fn a() (WithCompletionHandler stripped)
  • Enum: struct NSAccessibilityAnnotationPosition(NSInteger), Swift usually creates a nested items like NSAccessibility::AnnotationPosition, but that is not possible in Rust
  • Enum constant: impl NSAccessibilityAnnotationPosition { const fullRange: Self = ... }
  • Struct: struct NSDirectionalEdgeInsets { fields* }
  • Constant: impl NSModalResponse { const stop: Self = 1000 }
  • Typedef: struct NSBitmapImageRepPropertyKey(NSString)
  • Static: impl NSBitmapImageRepPropertyKey { fn currentFrameDuration() -> &'static Self }, associated statics are not possible in Rust yet so we must create a new function that can return it (also if we want to make it safe)
  • Function: impl NSWindowDepth { fn bitsPerPixel() -> NSInteger }
  1. Come up with a custom, "Rusty" scheme based on option 2
  • Framework: foundation
  • Class: Same
  • Protocol: Same
  • Category: Same
  • Method: fn some_long_method_name(method: i32), method name is made always lowercase (note: difficult now to tell which selector it corresponds to)
  • Method returning error: Same
  • Method with callback: Same
  • Enum: Same
  • Enum constant: impl NSAccessibilityAnnotationPosition { const FULL_RANGE: Self = ... }, constant is uppercase
  • Struct: Same
  • Constant: impl NSModalResponse { const STOP: Self = 1000 }, constant is uppercase
  • Typedef: Same
  • Static: impl NSBitmapImageRepPropertyKey { fn current_frame_duration() -> &'static Self }, function is made lowercase, in the future if we get associated statics then the name would change to be uppercase
  • Function: impl NSWindowDepth { fn bits_per_pixel() -> NSInteger }, function is made lowercase

I like option 1 since it'll mean that things are more "searchable"/"grepable". This point also somewhat goes for option 2, though a bit less so, since Swift is newer. The downside to both of these is of course that the names don't really match Rust's naming conventions.

Implementation wise: Option 1 is already implemented, so that's easy. Option 2 has seen a lot of work already by Apple/Swift developers, and they've even created the .apinotes system to help with edge-cases. Option 3 would be quite easy on top of 2, but would require a bit extra work if we want to tweak names further afterwards.

@madsmtm madsmtm added the question Further information is requested label Nov 4, 2022
@madsmtm
Copy link
Owner Author

madsmtm commented Nov 4, 2022

Also discussed previously in the metal crate: gfx-rs/metal-rs#190

@madsmtm madsmtm added the documentation Improvements or additions to documentation label Nov 4, 2022
@madsmtm
Copy link
Owner Author

madsmtm commented Nov 4, 2022

I think we should stick with Objective-C naming at the very least until we can autogenerate documentation based on https://developer.apple.com/, since it's just much more discoverable that way - but I can be persuaded otherwise!

@madsmtm
Copy link
Owner Author

madsmtm commented Nov 5, 2022

The windows crate is also a good place to look to for how this is done elsewhere.

@martial-plains
Copy link

So far going through the Apple's documentation I've only came across one instance of where an instance method and type method has had the same name. The NSObject protocol has an instance method called class while the NSObject interface has the type method class.

What do you think about having suffixes for methods like ip_ for instance property and tp_ for type property?

@madsmtm
Copy link
Owner Author

madsmtm commented Nov 11, 2022

instance method and type method

I'm a bit unsure about your terminology here; do you mean "instance method" and "protocol method"?


In general my opinion is that adding prefixes or suffixes is unnecessary - to the user, it doesn't really matter if something is a method or a property, or defined on a class or on a protocol, the way they're used is exactly the same.

Take for example +[NSThread isMainThread] and +[NSThread isMultiThreaded]: Here, the former is a property and the latter is a method, but I'd have no way of guessing which was which.

Similarly, I don't think we need to differentiate between class and instance methods, since their usage is different enough from Rust that it is already quite clear: NSThread::new() calls a class method, while obj.name() calls an instance method.

Of course, duplicate names do occur in such a way that we must change (e.g. NSThread has both +[NSThread isMainThread] and -[NSThread isMainThread]), but I think they are rare enough that we can do it on a case-by-case basis (so e.g. have NSThread::class_is_main_thread() and thread.is_main_thread()).

@martial-plains
Copy link

I understand where your coming from and you made a good point :)

@madsmtm madsmtm added the A-framework Affects the framework crates and the translator for them label Dec 23, 2022
@madsmtm
Copy link
Owner Author

madsmtm commented Jan 6, 2023

Another possibility is to namespace enums, structs and typedefs behind modules (when it makes sense):

  • Enum: mod ns_accessibility { struct AnnotationPosition(NSInteger) }
  • Struct: struct NSDirectionalEdgeInsets { fields* }
  • Typedef: mod ns_bitmap_image_rep { struct PropertyKey(NSString) }

I considered namespacing the same was as Swift, e.g. mod NSBitmapImageRep { struct PropertyKey(NSString); }, but we can't do that since it'd conflict with the NSBitmapImageRep class (again, no way to make associated types on structs).

Though perhaps we could then do mod NSBitmapImageRep { struct Class(...); } for classes instead?

@madsmtm
Copy link
Owner Author

madsmtm commented May 26, 2023

cidre uses the following scheme:

mod ns {
    struct Array<T>; // NSArray
    struct String; // NSString
    struct Number; // NSNumber
}

// Usage
use cidre::ns;

let array: ns::Array<ns::String> = ns::Array::new();

Which works pretty good for Foundation, but I'm not sure how well it extends to e.g. AppKit?

@madsmtm
Copy link
Owner Author

madsmtm commented Jun 20, 2023

I've pushed some of the objc2::runtime types towards a more Swifty naming scheme in #463.

@madsmtm
Copy link
Owner Author

madsmtm commented Jun 2, 2024

I've pondered this for a while now, but at this point I feel like sticking close to Objective-C for method and function names is the correct approach, even though it's less "Rusty".

That said, we still need to do some more work on the enum case prefix stripping, eliminating needless words in methods and on category naming.

Currently, I think the naming scheme is going to be:

  • Framework: objc2-foundation
  • Class: struct NSObject
  • Protocol:
    • struct NSCopying
    • struct NSObjectProtocol (renamed to not conflict with NSObject)
  • Category:
    • In same framework: impl Foo {} (inherent implementation)
    • If category on NSObject: trait NSNibAwaking (emit category name as-is)
    • Manually special-case categories that are defined multiple times: trait NSAffineTransformAdditions (AppKitAdditions)
    • Categories without a name: Disallow.
    • Otherwise: trait NSAccessibility
  • Method:
    • #[method(someMethod:withExtraParam:)] fn someMethod_withExtraParam(arg: i32, extra: u32)
    • Strip names according to mostly the same rules as Swift
      • Returning error: #[method(aMethod:error:)] fn aMethod(arg: i32) -> Result<(), Id<NSError>>
      • Completion handlers after async support #279, #[method(aMethodWithCompletionHandler:)] async fn aMethod()
    • Add _class when class methods conflict with instance methods.
  • Enum: struct NSAccessibilityAnnotationPosition(NSInteger)
  • Enum constant: impl NSAccessibilityAnnotationPosition { const FullRange: Self = ... } (the hope is that they will one day be closer to real enums)
  • Struct: struct NSDirectionalEdgeInsets { fields* }
  • Constant:
    • On NS_TYPED_EXTENSIBLE_ENUM, when possible: impl NSModalResponse { const Stop: NSModalResponse = ... }
    • Otherwise: const NSModalResponseStop: NSModalResponse = ...
  • Typedef: type NSBitmapImageRepPropertyKey = NSString
  • Static: extern "C" { static NSImageCurrentFrameDuration: NSBitmapImageRepPropertyKey }
    • If, in the future, Rust gives us safe external statics and/or associated statics, then we can consider using those.
    • But creating a helper function is not an option, since that will loose the static-ness of the value.
  • Function: extern "C" { fn NSBitsPerPixelFromDepth(depth: NSWindowDepth) -> NSInteger }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-framework Affects the framework crates and the translator for them documentation Improvements or additions to documentation question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants