{"action":"created","issue":{"url":"https://api.github.com/repos/ceylon/ceylon-spec/issues/1359","labels_url":"https://api.github.com/repos/ceylon/ceylon-spec/issues/1359/labels{/name}","comments_url":"https://api.github.com/repos/ceylon/ceylon-spec/issues/1359/comments","events_url":"https://api.github.com/repos/ceylon/ceylon-spec/issues/1359/events","html_url":"https://github.com/ceylon/ceylon-spec/issues/1359","id":89605060,"number":1359,"title":"Reified/Runtime Type Aliases","user":{"login":"jvasileff","id":236050,"avatar_url":"https://avatars.githubusercontent.com/u/236050?v=3","gravatar_id":"","url":"https://api.github.com/users/jvasileff","html_url":"https://github.com/jvasileff","followers_url":"https://api.github.com/users/jvasileff/followers","following_url":"https://api.github.com/users/jvasileff/following{/other_user}","gists_url":"https://api.github.com/users/jvasileff/gists{/gist_id}","starred_url":"https://api.github.com/users/jvasileff/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jvasileff/subscriptions","organizations_url":"https://api.github.com/users/jvasileff/orgs","repos_url":"https://api.github.com/users/jvasileff/repos","events_url":"https://api.github.com/users/jvasileff/events{/privacy}","received_events_url":"https://api.github.com/users/jvasileff/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/ceylon/ceylon-spec/labels/feature","name":"feature","color":"02e10c"},{"url":"https://api.github.com/repos/ceylon/ceylon-spec/labels/request%20for%20feedback","name":"request for feedback","color":"0b02e1"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":13,"created_at":"2015-06-19T16:26:26Z","updated_at":"2015-06-29T15:30:47Z","closed_at":null,"body":"With reified generics, you can obtain an instance of `Type<T>` from a generic argument `T`. This proposal is to support the reverse: define a reified type alias `T` with an instance of `Type<T>`.\r\n\r\n```ceylon\r\nType<Foo> someFooType = ...\r\nalias F = someFooType; // F satisfies Foo\r\n```\r\nReified (runtime?) type aliases would resemble type arguments. In fact, the behavior and capabilities of `T` and `U` in the example below would be indistinguishable:\r\n```ceylon\r\nString doSomething<U>(U arg) given U satisfies Object {\r\n    return ...\r\n}\r\n\r\nString callSomething(Type<Object> theType, Anything arg) {\r\n    // current (very unsafe!)\r\n    assert(is String result = `function doSomething`.invoke([theType], arg));\r\n    return result;\r\n\r\n    // with reified type aliases:\r\n    alias T = theType; // T satisfies Object\r\n    assert (is T arg); // explicit, documentable point of failure (much better!)\r\n    return doSomething(arg); // a normal, mistake free, type-checked invocation\r\n}\r\n```\r\n\r\nAn immediate benefit would be a greatly reduced need to use `apply` and `invoke` functions for meta programming, and a faster, safer bridge from meta to normal type checked code. I imagine a further benefit would be a tendency to create safer and more convenient apis due to the increased ability to use *type arguments* instead of *`Type<>` arguments*.\r\n\r\nNote: another advantage above is that `doSomething` could have been a local declaration, which is not possible when a meta model reference is required.\r\n\r\nA couple usage examples:\r\n\r\n## Example 1: Type Conversion Service\r\n\r\n`ConversionService` provides functions to convert/coerce values from one type to another. `toArray` converts any `Iterable` to an `Array` of the required type, converting elements as necessary.\r\n\r\n```ceylon\r\nshared interface ConversionService {\r\n    shared formal To? convert<To>(Anything from);\r\n\r\n    // New style:\r\n    shared formal To(From)? converter<From, To>();\r\n\r\n    // Current style (not used in the example):\r\n    shared formal Anything(Nothing)? converterForTypes(\r\n            Type<Anything> from,\r\n            Type<Anything> to);\r\n}\r\n\r\n\"Convert [[items]] to an [[Array]] of type [[To]] (e.g. `Array<Integer>`).\r\n Return [[null]] if [[To]] is not an Array type, or if the element\r\n type of [[items]] cannot be converted to the element type of [[To]].\"\r\nshared To? toArray<To>(\r\n        {Anything*} items,\r\n        ConversionService conversionService) {\r\n\r\n    value toType = `To`;\r\n    if (is ClassOrInterface<Array<out Anything>> toType) {\r\n        assert (exists toElementType = toType.typeArgumentList.first);\r\n        assert (exists fromElementType = typeOfIterableElementFromInstance(items));\r\n\r\n        alias ToElement = toElementType;\r\n        alias FromElement = fromElementType;\r\n\r\n        ToElement(FromElement)? elementConverter =\r\n                conversionService.converter<FromElement, ToElement>();\r\n\r\n        if (exists elementConverter) {\r\n            // Option #1, with ceylon-spec/issues/560\r\n            assert (abstracts Array<ToElement> To);\r\n            return Array(items.map(elementConverter));\r\n\r\n            // Option #2:\r\n            assert (is To result = Array(items.map(elementConverter)));\r\n            return result;\r\n        }\r\n    }\r\n    return null;\r\n}\r\n```\r\n\r\nThis much more closely resembles a normal Ceylon program, rather than an alternate approach using `invoke`. And, importantly, the assertions are 1) explicit, and 2) easy to reason about. There are no calls to functions that may throw because we provided the wrong argument to a parameter of type `Type<Anything>` or `Anything`.\r\n\r\n(See also https://github.com/ceylon/ceylon-spec/issues/560)\r\n\r\n## Example 2: Configuration Service\r\n\r\nThis example is adapted from `ceylon/formatter/options/formattingFile_meta.ceylon` and contrasts style and error handling between the two approaches.\r\n\r\n```ceylon\r\nclass Settings() {\r\n    shared variable Boolean? booleanSetting = null;\r\n    shared variable Integer? integerSetting = null;\r\n    shared variable Float? floatSetting = null;\r\n}\r\n\r\n// This utility function only makes sense with runtime aliases.\r\n// (it's not nearly as useful if both \"Return\" and\r\n// \"Arguments\" need to be known statically.)\r\nFunction<Return, Arguments>? findFunction<Return, Arguments>\r\n        (Package container, String functionName)\r\n        given Arguments satisfies [Anything*] {\r\n    try {\r\n        return container.getFunction(functionName)\r\n                ?.apply<Return, Arguments>();\r\n    }\r\n    catch (_) {\r\n        return null;\r\n    }\r\n}\r\n\r\n// For completeness (not relevant to this issue):\r\nString nameOfParseFunction(Type<Anything> type) {\r\n    String fullTypeString = type.intersection(`Object`).string;\r\n    Integer? endOfPackageIndex = fullTypeString.inclusions(\"::\").first;\r\n    assert (exists endOfPackageIndex);\r\n    String trimmedTypeString = fullTypeString[endOfPackageIndex+2 ...].trim('?'.equals);\r\n    return \"parse\" + trimmedTypeString;\r\n}\r\n\r\n// Using current api and language features:\r\nvoid setOld(Settings settings, String option, String optionValue) {\r\n    \"invalid option\"\r\n    assert (exists attribute = `Settings`.getAttribute<Settings>(option));\r\n\r\n    \"parse function of expected name not found\"\r\n    assert (exists parseFunction =\r\n            `package ceylon.language`.getFunction(\r\n            nameOfParseFunction(attribute.type)));\r\n\r\n    // fail if parse function argument type is not `String`\r\n    value applied = parseFunction.apply<Anything, [String]>();\r\n\r\n    // fail if parse function result type is wrong\r\n    // fail if attribute is not settable\r\n    Anything parsedOptionValue = applied(optionValue); // Note Anything result\r\n    attribute(settings).setIfAssignable(parsedOptionValue);\r\n}\r\n\r\n// Using runtime/reified aliases:\r\nvoid setNew(Settings settings, String option, String optionValue) {\r\n    \"invalid option\"\r\n    assert (exists attribute = `Settings`.getAttribute<>(option));\r\n\r\n    alias AttributeType = attribute.type; // runtime alias\r\n\r\n    \"option not settable\"\r\n    assert (is Attribute<Settings, AttributeType, AttributeType> attribute);\r\n\r\n    \"parse function not available\"\r\n    assert (exists parseFunction = findFunction<AttributeType, [String]>(\r\n                `package ceylon.language`,\r\n                nameOfParseFunction(attribute.type)));\r\n\r\n    AttributeType parsedOptionValue = parseFunction(optionValue); // Note AttributeType result\r\n    attribute(settings).set(parsedOptionValue);\r\n}\r\n\r\nshared void tryIt() {\r\n    Settings settings = Settings();\r\n    setOld(settings, \"integerSetting\", \"10\");\r\n    print(settings.integerSetting);\r\n    setNew(settings, \"integerSetting\", \"20\");\r\n    print(settings.integerSetting);\r\n}\r\n```"},"comment":{"url":"https://api.github.com/repos/ceylon/ceylon-spec/issues/comments/116732677","html_url":"https://github.com/ceylon/ceylon-spec/issues/1359#issuecomment-116732677","issue_url":"https://api.github.com/repos/ceylon/ceylon-spec/issues/1359","id":116732677,"user":{"login":"jvasileff","id":236050,"avatar_url":"https://avatars.githubusercontent.com/u/236050?v=3","gravatar_id":"","url":"https://api.github.com/users/jvasileff","html_url":"https://github.com/jvasileff","followers_url":"https://api.github.com/users/jvasileff/followers","following_url":"https://api.github.com/users/jvasileff/following{/other_user}","gists_url":"https://api.github.com/users/jvasileff/gists{/gist_id}","starred_url":"https://api.github.com/users/jvasileff/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jvasileff/subscriptions","organizations_url":"https://api.github.com/users/jvasileff/orgs","repos_url":"https://api.github.com/users/jvasileff/repos","events_url":"https://api.github.com/users/jvasileff/events{/privacy}","received_events_url":"https://api.github.com/users/jvasileff/received_events","type":"User","site_admin":false},"created_at":"2015-06-29T15:30:47Z","updated_at":"2015-06-29T15:30:47Z","body":"Yeah, nice examples. I think we'd still need an `assert` on `container`:\r\n```ceylon\r\nalias ContainerType = type(container);\r\nassert (is ContainerType container);\r\n```\r\nunless we stipulate cases where the narrowing would be implied (basically when using the result of `type`  on a non-variable reference.) Obviously it would be safe here. Maybe there should be some accommodation for this in the syntax."}}