Search code examples
purescript

How do you create an Effect in Purescript directly via its type constructor?


Is it possible to use the Effect constructor directly? e.g. foo = Effect "howdy!"

It seems like this should be possible. It appears to be a type constructor.

:kind Effect
Type -> Type 

However, if I try to construct it an error gets thrown

Effect 1234
Unknown data constructor Effect

I can only create one 'indirectly' with pure

myeffect :: Effect Int 
myeffect = pure 123

Am I missing something?


Solution

  • This is not how types work

    The signature Effect :: Type -> Type means that if you take the word Effect and attach a type to the right of it, the result of that will be another type. So:

    Effect :: Type -> Type
    Int :: Type
    Effect Int :: Type
    

    This does not say anything about creating values of type Effect. That is:

    Effect Int :: Type
    
    x :: Effect Int
    x = Effect 42  -- not necessarily allowed
    

    For many types this happens to work just fine, for example:

    Maybe Int :: Type
    
    x :: Maybe Int
    x = Just 42   -- perfectly valid
    

    But it doesn't have to. It all depends on how the type is defined. For example:

    data MyType a = Foo
    
    MyType :: Type -> Type
    MyType Int :: Type
    x = Foo 42   -- not allowed
    y = Foo      -- allowed
    

    Note that I use the word Foo to construct values of MyType, not the word MyType. This is because they are different things. MyType is the name of the type, and Foo is the name of a constructor - which is a function that can construct (hence the name) values of a type. Other examples of constructors are Just and Nothing - both used to construct values of type Maybe.

    A constructor may have the same number of parameters as the type itself, or it can have fewer parameters, or more. For example:

    data Type1 a = Ctor1 a
    data Type2 a = Ctor2
    data Type3 a = Ctor3 a a
    
    x :: Type1 Int
    x = Ctor1 42 
    
    y :: Type2 Int
    y = Ctor2
    
    z :: Type3 Int
    z = Ctor3 42 84
    

    So, to summarize: depending on how the type is defined, you may or may not use the name of the type to create values of that type, the way you're trying to do with Effect.

    (also, some types may have multiple constructors, e.g. data Maybe a = Just a | Nothing, but this is already taking too long)

    But all of this doesn't even apply to Effect, because...

    This is not how Effect works

    Effect does not have any constructors at all. There is absolutely no way to directly create values of this type, regardless of parameters.

    This is because Effect is magic. A value of type Effect Int is definitely NOT a sort of "box" that contains an Int inside. No-no-no.

    Instead, a value of type Effect Int is a program, which, when executed, will eventually produce an Int. You can't look inside it, you can't take it apart, the only thing you can do with it is execute it.

    And how do you execute it? Easy! You return it from your main function!

    No, seriously, that's the only way to execute an Effect. (well, ok, there's also unsafePerformEffect, and then you can do it with FFI, but those are hacks for the initiated, don't go there)

    The normal way you execute an effect is to return if from your main function, and the runtime will take care of it. Boom!

    Another way to look at it is that your whole program is one big (or small) Effect. That's your program.

    Another thing you can do with effect is to combine it with another effect using operator >>= (or its evil twin =<<). For example:

    x = pure 40
    y = x >>= \a -> a + 2
    
    z = pure 42
    

    Here, y and z are equivalent programs: both, when executed, will produce number 42.

    And this is the way you write programs in PureScript: you start with built-in ("magic") functions that produce effects, such as cwd or readLine or whatever, and then compose those effects with others via >>= or =<< or the do notation (which is syntactic sugar for >>=). Then, the resulting big effect, constructed out of many small ones, you return from your main function - and voila, you got yourself a program!