Standard type extraction
In Schema we now added ability to extract types like: typeof Person.Type that makes it really nice to get the types, the same should be applied to things such as tags so one can do typeof Service.Type and to Effect so that one can do typeof Effect.Success etc.
I do feel though that unless we prefix those fake properties somehow it can become confusing, one might argue that defining them in uppercase makes it clear that they are meant to be types and not real values.
This issue is to discuss on a standard.
cc @gcanti @tim-smart @IMax153 @datner @patroza @fubhy
So far I think that the capitalization is sufficient. Types are generally capitalized. Uppercase generally signifies constants. JSDoc should take care of the rest.
If we wanted to be even more pedantic, we could add a Type suffix, so:
Effect.SuccessType
Effect.ErrorType
Effect.ContextType
but to be honest I think this is unnecessary and overly verbose. I agree with @patroza that the capitalization should generally be sufficient, along with a JSDoc explaining what the value is intended to be used for.
One other option is a namespace:
typeof someEffect.Types.Success
But I think I prefer .Success etc directly on the type.
One other option is a namespace:
typeof someEffect.Types.Success
lol you mad..
can never have enough namespaces can we, we'd be back at square one: Effect.Effect.Success<typeof X>.
then again it might be preferred over pre or suffixes, and it might address mikes concerns.
while
typeof someEffect.Types.Success
would be the most obvious, the more nested it is the less benefit it has over Effect.Effect.Success<typeof someEffect>
Like, if we just alias it to the module level then compared with an Effect.Success<typeof someEffect> it's barely any different.
It has to be
typeof someEffect.Success
if at all 🤔 (name pending, I only mean the nesting)
I would also offer to consider lifting the stand-alone type accessors from the namespace to the top-level. I often use bare types for polymorphism, and my codebase is full of
const exchangeCtx = yield* Effect.context<
Layer.Layer.Success<ExchangeLayer>
>()
However, there is nothing preventing us to have Layer.Success, because there are no conflicts on the top-level namespace. In the end of the day – having a nested namespaces feels cumbersome
@dilame I would very confidently place the usage of Effect.context to be in the bottom slots of the "very rare" bracket of utils, so I can't quite imagine how you get a 'codebase full of' it. Regardless, the question at hand is not about eliminating the nested namespace. That would not happen most likely as the purpose of it is to enrich the base type Effect (aka Effect.Effect) with utilities like Effect.Success (aka Effect.Effect.Success). The question is about not needing utilities at all.
In your case, it would mean typeof ExchangeLayer.Success, not Layer.Success<typeof ExchangeLayer>. If you seek the latter, just change
- import { Layer } from "effect"
+ import { Layer } from "effect/Layer"
to get Layer.Success<typeof ExchangeLayer>
I think Effect.Success, Effect.Error and Effect.Context is fine
@datner it will work when you are relying on runtime entities, but will not work when you use Layer as kind of OOP polymorphism (which i believe is the original intention of layers)
export type ModeLayer = Layer.Layer<
| ModeKlineImpl
| ModeOrderCancelImpl
| ModeOrderCreateImpl
| ModeOrderFetchImpl
| ModeOrderListenImpl
| ModePriceImpl,
never,
Layer.Layer.Success<ExchangeLayer>
>;
@dilame I honestly have no clue what you're showing me, I don't have code that looks like that..
Remember that effects patterns are still emerging, there are very few blessed ways to use effect and they are largely technically driven and don't force a specific design so it's not odd we'll find different patterns, but I have no idea why would you want / need that code 😅
I'm just saying that it's a different conversation, and currently it's impossible to merge namespaces in typescript (we tried, really hard). Lets refocus please, do you have any opinions regarding typeof myEffect.Succeed? Do you think it should exist?
@datner it will work when you are relying on runtime entities, but will not work when you use
Layeras kind of OOP polymorphism (which i believe is the original intention of layers)export type ModeLayer = Layer.Layer< | ModeKlineImpl | ModeOrderCancelImpl | ModeOrderCreateImpl | ModeOrderFetchImpl | ModeOrderListenImpl | ModePriceImpl, never, Layer.Layer.Success<ExchangeLayer> >;
Very much not the intended usage of layers. A layer represent a layer in the sense defined by the onion architecture, so a progressive translation of implementation from most abstract to least. Never seen a layer being manually typed, and also having *Impl in the signature is kind of bad, it should be the generic interface not the specific impl
Very much not the intended usage of layers. A layer represent a layer in the sense defined by the onion architecture
I believe that OOP polymorphism and Effect Layer are both low-level abstractions aimed at achieving the goals of high-level architectures like Onion, Ports and Adapters, Hexagonal, Whatever. These paradigms emphasize separation of concerns and dependency inversion. Thus, I see Effect Layer as an alternative to OOP polymorphism within these architectural frameworks. They are not opposing concepts but tools that serve the same purpose in maintaining a clean architecture.
it's not odd we'll find different patterns, but I have no idea why would you want / need that code 😅
You are so tactful and real at the same time, i like it!
Lets refocus please, do you have any opinions regarding typeof myEffect.Succeed? Do you think it should exist?
I have no doubt that typeof myEffect.Succeed should exist.
Practically speaking, to get the type of myEffect at runtime, we have to write typeof myEffect anyway. If we extract generics using another generic type, the best case would look like this:
import { Success } from 'effect/Effect'
Success<typeof myEffect>
This adds Success and two extra symbols < and >. Typically, imports are import { Effect } from 'effect', making the extraction code even longer.
typeof myEffect.Succeed just adds single dot symbol and requires no extra imports. I vote with three hands for typeof myEffect.Succeed.
Very much not the intended usage of layers. A layer represent a layer in the sense defined by the onion architecture
I believe that OOP polymorphism and Effect Layer are both low-level abstractions aimed at achieving the goals of high-level architectures like Onion, Ports and Adapters, Hexagonal, Whatever. These paradigms emphasize separation of concerns and dependency inversion. Thus, I see Effect Layer as an alternative to OOP polymorphism within these architectural frameworks. They are not opposing concepts but tools that serve the same purpose in maintaining a clean architecture.
Even following your logic a sentence like @datner it will work when you are relying on runtime entities semantically has no meaning (makes no sense), code like the one you posted is not common and I'd go as far as to say it's even an anti-pattern. Anyway all of this is fairly off topic, feel free to continue the discussion via discord, this issue only talks about type extractors and I think we reached agreement on the team that type of Effect.Success is a valid option
Hmm we will have to change the variance in some scenarios to make this work:
It also seems to break the Effect.Success assignability where void is expected.
@tim-smart I think this technique is applicable only to covariant and invariant type parameters (as is the case with @effect/schema)
We might be able to use the same trick as the Iterators to disconnect the type helpers from the type variance. Will have a play around tomorrow.
We might be able to use the same trick as the Iterators to disconnect the type helpers from the type variance. Will have a play around tomorrow.
Not sure that's possible
An alternative would be to have a general GetType helper to use like: GetType<typeof program>["Success"] which can be made by enhancing the supported types with an extractor symbol that preserves the variance, it's a bit more involved than simply typeof program.Success but it can be made truly general, ideally we could even shorten it to like Type<typeof program>["Success"]
Maybe just moving them to the top level is the simplest approach?