I am looking a bit at Ccs and I don't understand why getService does not return IService instead of P.L.Object. In fact, what is the point of IService if not for having a return type for getService?
It’s primarily to allow the Service Manager to act as a factory for many types, and not to have the requirement that everything in an application is of type Ccs.Common.IService (since the implementer may not have control over all the code in an application). You’ll also note that the spec has this interface as an optional (not required) interface.
Ideally it should return the type as requested for the service-name.
It’s primarily to allow the Service Manager to act as a factory for many types, and not to have the requirement that everything in an application is of type Ccs.Common.IService (since the implementer may not have control over all the code in an application). You’ll also note that the spec has this interface as an optional (not required) interface.
Ideally it should return the type as requested for the service-name.
Aah, I didn't think about services that happen to exist and were not designed with Ccs in mind. That makes sense, thanks for the clarification.
> Ideally it should return the type as requested for the service-name.
That'll require generics I guess?
|
I still don’t think the service manager should behave as a factory for ‘everything’ and, although the IService interface doesn’t really add much, the getService method should indeed return a so called ’service’.
A generic method (as in .NET) would look like
<T> ServiceManager:getService<T> () .
and not
Progress.Lang.Object ServiceManager:getService (T) .
There are good use cases for generic classes (like described by Marian with the ServiceManager<T> and generic methods as described here.
ABL does currently support using .NET Generic types - but not using .NET generic methods in non generic types (unless you go for Reflection: github.com/.../ReflectionHelperOSS )
Argh, not a big fan of the generics methods from .net but that might be just because of my background… I found them just like a more pretentious version of inline casting, generic classes are something else and here I totally agree with you guys that we need those in the 4GL (not just when using .net).
Get over it, Marian!
It's more than just casting! It's guaranteeing, that the type you request and the type the result is (inline casted to) are the same.
Marian, generics would allow this:
define variable interestingService as ISomeInterestingServiceType no-undo.
assign interestingService = Ccs.Common.Application:ServiceManager:getService<>();
Compare that to the current cast stuff and tell me what you like best ;-) There are other use cases where the advantage is bigger (type safety in collection classes comes to mind) as this specific problem can mostly be overcome with a small include but still, it would be a lot cleaner if the syntax were build into the language rather than bolted on.
ps.: Logic in includes: Yuck.
As said not a big .net fan but I was under the impression the generics methods in .net will require something like this…
Knowing you well enough, I’m sure you will. It’s still early in the morning and already late in the week. So a little bit of pointless Microsoft bashing is forgiven.
Why limit yourself to .NET generics syntax if you have the freedom to design it like it could be. The compiler knows the type of the variable being assigned, so why force repeating it. Less code to maintain and read all the time tends to be better if no confusion is possible.
ps.: The method calling syntax I suggested works fine in Java. Results in very clean code.
So you have a better proposal than what's proven to be very helpful in .NET?
ps.: Another reason why generics can be better than just casting: docs.microsoft.com/.../constraints-on-type-parameters
You can limit the types that are acceptable to return, so you can make your generic method type-safe at compile time.
That’s a good one Jan, you might have been tasting more wine than I did… but you’re right, let’s make the compiler great again ;)
> So you have a better proposal than what's proven to be very helpful in .NET?
Was that a question for me because I said not to limit yourself to .NET syntax? I don't know much .NET but if this is required in .NET-like syntax:
assign interestingService = Ccs.Common.Application:ServiceManager:getService< ISomeInterestingServiceType >().
Than I suggest this:
assign interestingService = Ccs.Common.Application:ServiceManager:getService<>().
(like it's done in Java by the way)
So the <T> parameter is determined by variable it's assigned to? Don't think I like that (and Marian, I was not near a winery yesterday). In case of subtyping I can see cases where being explicit on the type you request is important.
Jan, where do you see a reference to a Java generic method with no parameters?
docs.oracle.com/.../methods.html
So me it is seems that Java is determing the type parameters from the actual method arguments. But that would always require an argument....
Hmm, I may have it confused with the constructor syntax ( SomeClass<Integer, String> sometobject = new SomeClass<>(); does work, no time to check the method right now, but there is no reason why it couldn't work.)
I feel like listing the type again if it is already known from the variable that's being assigned is like suggesting this for a for each on a buffer:
for each bufCustomer <customer> where bufCustomer.custno <customer.custno> = 123 :
...
end.
Since the buffer is defined on the customer table there's now reason to list it over and over again, why would we insist on doing exactly that when we are referring to variable types? You should allow it (for when you are not storing the result of a method but directly calling a method on the result for example) but I see no reason to enforce it if the compiler can figure it out. Again, as long as it is clear and no confusion is possible, why add overhead to code. If it doesn't add anything I don't think it should be there.
Buffers have not super and sub types. IMHO a significant difference for your case.
The only problem there Jan is how do you know, from generic method, what you should return to the caller? Mike is right, in Java generic methods only work with type parameters so inside the method you know what need to be returned from the type of input parameter(s).
getService<T>('myService') is pointless since we already have casting. (although imho the CAST function looks ugly).
assign interestingService = Ccs.Common.Application:ServiceManager:getService<>(). is no good as well, since that
throws away the concept of Strong Typing (if getService returns a P.L.O. you want the compiler to tell you that you're mixing up the types, that's the whole point of Strong Typing).
The other way around would be nice and is called Type Inference. This would mean something like:
def var myService = serviceManager:GetService('logservice').
Now if the GetService method returns an IService type, the myService var would be automatically of the type IService.
Something like this should work:
method public <T> getService ( ):
find first ttservice where get-class(ttservice.service).isA(T) no-error.
if avail ttservice then return cast( ttservice.service , T).
else undo, throw....
end.
What's the point of the generics if you still need to cast? The point is that you only need it once in a single place where you can add rigorous type checks and testing instead of polluting all your code on every call.
Mike, as said, you should be able to modify the type, ie in the call for cases of inheritance and such, but assuming that inheritance is not overused, I expect that to be needed only in the exception rather than the general case.
Generics are meant for run compile time type checking, what you're showing is still runtime behaviour. The undo, throw is the same what you would get is the cast doesn't work out because incompatible types. Generics are handy when you use it to make sure what you put into a class is of Type T. For example:
def var list as ArrayList<IService> no-undo.
list = new ArrayList<IService>().
list:Add(bla). // fails on compile time if you put anything else than an IService in it.
And then it would be handy to have an:
def var myService as IService no-undo.
myService = list:get('service_abc'). // because get returns T
But only because you know (because of generics) that there are only T's in the collection.
That will work if you define the class to be generic, aka ServiceManager<T>… or ServiceManager<T extends IService> to be more precise :)
All I am saying is that a syntax like this:
assign interestingService = Ccs.Common.Application:ServiceManager:getService<>().
and
Ccs.Common.Application:ServiceManager:getService<ISomeInterestingService>():doSomething().
calling a method like this:
method public <T> getService ( ):
...
end.
could be made to work in ABL and would result in very clean code in the users of the calling service.