When designing a API, what principles should I fol...
# codingconventions
t
When designing a API, what principles should I follow to choose whether given type should be SAM or typealias for a function type?
j
I've made the experience that type aliases for complex functions slow down intellij by a lot (especially when using them in vararg positions). Either way the interface seems clearer to me as it allows you to change it more freely later on if needed
1
t
@Jannis I don't think IDE performance should matter when designing API. It's IDE issue, not mine. So, should we always use SAM interfaces instead of function types, except of interoperability reasons?
j
I don't think IDE performance should matter when designing API. It's IDE issue, not mine.
It does matter tho. Believe me I'd like it not to, but if you want someone (even if thats you yourself) to use your api better make damn sure its, well, usable from within an ide. If the functions are complex SAM interfaces are easier imo, otherwise function types will be just fine. Not sure how SAM interfaces fare vs normal function types in terms of performance tho. Also iirc SAM interfaces cannot be suspend, whereas function aliases can. So imo this is never a black/white kind of question and highly depends on what you want to do with that api.
t
It does matter tho. Believe me I'd like it not to, but if you want someone (even if thats you yourself) to use your api better make damn sure its, well, usable from within an ide.
It's should be a must for IDE developers to support all language features. Otherwise it's something wrong with either IDE or language and reason good enough to change former or latter.
Also iirc SAM interfaces cannot be suspend, whereas function aliases can.
I think supended SAM only got temporairly suspended( : P ). A reason good enaugh to temporarily use typealias, but in such cases I mark such declarations with an @OptIn annotation specific to pending language feature.
So imo this is never a black/white kind of question and highly depends on what you want to do with that api.
That's why I am looking for a principle. I think SAM should be preferred unless given declaration is purely for generic functional use, like
Sequence.map
r
IIRC, the SAM and typealias differ in usage right? You can't assign
(A) -> B
to SAM with
(A) -> B
function If that's true and I'm not talking out of my ass, then there is imho pretty clear distinction between the usage of the two. And that is - use SAM if and only if you require some additional "features" of the function, other than it's type. For example a
HashFunction
should most definitelly be a SAM, because not every
(String) -> String
function is a hash function - you also require the results to be well distributed, so you want to forbid user passing just any function, just those that implement HashFunction or direct lambda at the call On the other hand the functional usage does not require any additional features, so they should be either typealias or direct type usage.
1
t
@Roukanken Nice, I haven't thought about it this way. So, I refine my principles: Use typealias if you want to give friendly name to type, that already exist, without applying any new semantics to it. Ex. Consumer, Function, Transformation, Supplier. Use SAM if you need functional type with domain specific semantics. Ex. Listener, ActionHandler.
@Roukanken Any thoughts about SAM vs value class on function type?
Copy code
value class Hash(val hash : Long)
value class HashFunction<T>(val hash: (T)->Hash)
We can reduce delegation this way, if we could somehow cast between (T)->Hash and (T)->Long. This would play nicely if multiple libraries would define type for HashFunction.