mbw 2017-01-31 21:45:37
Would this change if I return $! () explicitly?
mbw 2017-01-31 21:45:49
(or whatever the result type is)
Cale 2017-01-31 21:45:49
hm?
Cale 2017-01-31 21:46:10
Well, () is already evaluated, so $! there is pointless
mbw 2017-01-31 21:46:24
ah, right
mbw 2017-01-31 21:46:48
Non-strictness requires some intellectual overhead...
Cale 2017-01-31 21:46:59
Now, usually you *can* get away using throw instead of throwIO in the middle of an IO action, and things will work
Cale 2017-01-31 21:47:30
Because usually the moment at which you're evaluating an IO action won't be very different from the moment at which you're executing it
Cale 2017-01-31 21:47:45
But it's best not to tempt the fates :)
mbw 2017-01-31 21:48:09
Ok, but that's the point, right? Taking non-strictness into account?
Cale 2017-01-31 21:48:40
Well, even if everything was strict, there would be a difference -- it would be a larger difference in fact
Cale 2017-01-31 21:48:50
In a strict setting, you wouldn't be able to get away with using throw
Cale 2017-01-31 21:48:56
as a substitute for throwIO
Cale 2017-01-31 21:49:08
because the exception would occur while you were building the IO action, before you ran it
Cale 2017-01-31 21:50:13
(actually, perhaps not, because even the eagerest evaluators don't tend to evaluate inside unapplied lambdas, but it would still often go off earlier than you wanted)
Cale 2017-01-31 21:50:25
For example, if we imagine that (>>) is strict
Cale 2017-01-31 21:50:46
Then print 5 >> error "foo!"
Cale 2017-01-31 21:51:05
would result in the error occurring before you ever had a chance to print anything
mbw 2017-01-31 21:52:04
So print 5 would be reduced up until '()', and then the exception will be thrown?
Cale 2017-01-31 21:52:24
print 5 would be evaluated into a description of how to print 5 on the screen
Cale 2017-01-31 21:52:29
without actually doing anything
Cale 2017-01-31 21:52:36
and then the exception would be thrown
Cale 2017-01-31 21:52:44
assuming we evaluate it from left to right
Cale 2017-01-31 21:53:34
The difference between a value of type IO String and a value of type String is like the difference between /bin/ls and a list of files in your home directory.
mbw 2017-01-31 21:54:13
This is an analogy I am unlikely to forget.
Cale 2017-01-31 21:54:22
Merely reading /bin/ls off of your disk, say, opening it in a hex editor, doesn't cause a list of files in your home directory to be printed
Cale 2017-01-31 21:54:35
Similarly, evaluating which IO action print 5 is, doesn't cause anything to be printed.
Cale 2017-01-31 21:55:28
It's hard to say precisely what happens, because IO is an abstract type, so we either get into GHC's hacky implementation using impure functions, or we make up something artificial
Cale 2017-01-31 21:55:53
Let's actually do an artificial and highly simplified version of IO
mbw 2017-01-31 21:56:05
So the claim that IO is pure, via passing RealWorlds around, isn't true?
mbw 2017-01-31 21:56:25
(Well it doesn't sound efficient, anyway)
Cale 2017-01-31 21:57:43
Depending on what you're referring to, the "passing RealWorlds around" thing is either just an analogy which is actually kind of a pretty bad mental model, or a hack to get GHC's dependency analyser to sequence the evaluation of impure functions that are used to implement IO actions in GHC
Cale 2017-01-31 21:58:24
In that second case, the State# RealWorld values which are being passed around are 0-bit-wide tokens that are eliminated by a later part of compilation.
mbw 2017-01-31 21:59:00
Yeah, that's what I was referring to. It looks like a state monad.
Cale 2017-01-31 21:59:07
kinda, yeah
Cale 2017-01-31 21:59:13
But there's impure functions everywhere
Cale 2017-01-31 21:59:41
and evaluation at that level really is causing stuff to happen then
Cale 2017-01-31 21:59:59
It's not very easy to think about
Cale 2017-01-31 22:00:16
But one could imagine an implementation where IO was an algebraic data type instead
Cale 2017-01-31 22:00:34
with constructors corresponding to various primitive actions that could be taken
Cale 2017-01-31 22:00:53
For example...
Cale 2017-01-31 22:01:25
data IO a = PutChar Char (IO a) | GetChar (Char -> IO a) | ...
Cale 2017-01-31 22:02:07
Here, the last parameter to each constructor would be a continuation which would say what to do next, possibly depending on the result of the action.
Cale 2017-01-31 22:02:26
oh, I should also include
Cale 2017-01-31 22:02:45
data IO a = Done a | PutChar Char (IO a) | GetChar (Char -> IO a) | ...
Cale 2017-01-31 22:03:10
Of course, evaluating something like PutChar 'a' (PutChar 'b' (Done ()))
Cale 2017-01-31 22:03:15
won't actually cause anything to happen
Cale 2017-01-31 22:03:37
We need some sort of interpreter which pattern matches on this representation, and actually does the stuff which is described
_sras_ 2017-01-31 22:04:07
If I have got two Template haskell functions A and B, that return Q [Dec], and if B dependes on code created by A, how can I wrap these two into a single TH function?
Cale 2017-01-31 22:04:22
and you can imagine that such an interpreter lives in the runtime system somewhere, pattern matching on main (initially) and then carrying out the action described and recursively interpreting whatever is left
mbw 2017-01-31 22:04:29
So in your example, IO describes an AST?
Cale 2017-01-31 22:04:41
vaguely
Cale 2017-01-31 22:04:53
It's like a higher order abstract syntax of sorts
Cale 2017-01-31 22:05:16
You have stuff like GetChar (\c -> PutChar c (Done ()))
Cale 2017-01-31 22:05:42
We can actually write this interpreter
tsahyt 2017-01-31 22:05:57
that vaguely reminds me of continuations
Cale 2017-01-31 22:06:13
Well, you would be right to call the argument to GetChar a continuation
thatguy 2017-01-31 22:06:33
I have a problem with newtype. As I understood it it is barely something like a type alias and behaves same as the type at runtime. Anyways, if I define newtype Book = Book Int and newtype Movie = Movie Int and a function testfunc :: Book -> Int, testfunc (Book a) = a, my testfunc cannot take Movies as arguments since it is the wrong time
thatguy 2017-01-31 22:06:51
but doesn't that mean that they do not forget at runtime what their type alias has been but have to remember?
tsahyt 2017-01-31 22:07:04
thatguy: no, type checking happens at compile time
tsahyt 2017-01-31 22:07:12
at runtime the two are indeed identical
lyxia 2017-01-31 22:07:47
_sras_: you could have B take some token that only A produces.
thatguy 2017-01-31 22:08:44
tsahyt, ah ok, so at runtime testunc would behave exactly the same as id :: Int?
thatguy 2017-01-31 22:09:07
but it would not be applied to anything of type Movie since it was checked at compile time that this shouldn't happen
mbw 2017-01-31 22:09:16
Why must it be a continuation? When you initially mentioned describing thinking about IO in terms of an algebraic data type, my first idea would have been something like a sum type with infinite cardinality, corresponding to all possible states, though of course that's not helpful at all...
lyxia 2017-01-31 22:09:17
_sras_: or simpler, what about liftA2 (++) A B
_sras_ 2017-01-31 22:09:24
lyxia: But I am getting the error "'UsersPoly' is not in scope at a reify". UsersPoly is defined by the first TH function...
tsahyt 2017-01-31 22:09:38
basically, yes. wrapping/unwrapping of a newtype is guaranteed to incur no runtime cost, and newtype T = T Foo has the same runtime representation as Foo.
mbw 2017-01-31 22:09:49
But the GetChar representation isn't really clear to me.
tsahyt 2017-01-31 22:10:17
so when you pass a newtype in somewhere and pattern match the contained value out, that part is free. newtypes give you additional type safety without additional cost.
lyxia 2017-01-31 22:10:45
_sras_: oh I see, so that forces you to call A and B separately in sequence
tsahyt 2017-01-31 22:10:47
other than syntactic overhead of course, but using generalized newtype deriving and type classes, even that often goes away.
_sras_ 2017-01-31 22:10:57
lyxia: Exactly
lyxia 2017-01-31 22:13:40
_sras_: maybe you could have a variant of A that also returns the reification of whatever type you want, and change B to take the output of reify instead of calling reify inside it.
runeks 2017-01-31 22:14:06
What's the trick needed to construct a list of types that are all instances of some specific class? I believe I read somewhere that it's possible, by wrapping each list item in something, but I can't remember where or how.
thatguy 2017-01-31 22:14:12
tsahyt, nice, thanks for helping!
_sras_ 2017-01-31 22:14:13
lyxia: I cannot change B. It is from another package.
reactormonk 2017-01-31 22:14:47
@djinn Except e m a -> ExceptT [e] m a
lambdabot 2017-01-31 22:14:48
Error: Undefined type Except
reactormonk 2017-01-31 22:14:52
@djinn ExceptT e m a -> ExceptT [e] m a
lambdabot 2017-01-31 22:14:52
Error: Undefined type ExceptT
reactormonk 2017-01-31 22:14:57
aww
reactormonk 2017-01-31 22:15:09
How would you write that function in a concise way?
_sras_ 2017-01-31 22:15:29
lyxia: In the first function, can I include a top level Function expression that makes TH call?
mbw 2017-01-31 22:15:48
Cale: Or is there some resource that expands on this idea?
lpaste 2017-01-31 22:15:54
Cale pasted "Toy IO monad" at http://lpaste.net/351859
mbw 2017-01-31 22:16:00
Ah.
Cale 2017-01-31 22:16:01
mbw: ^^ there you go :)
lyxia 2017-01-31 22:16:22
_sras_: I don't think so.
lyxia 2017-01-31 22:17:06
_sras_: what's the problem with calling A and B separately
Cale 2017-01-31 22:17:28
mbw: The instance of Monad for this is perhaps a little bit tedious to write out, but it's actually possible to abstract this a bit
Cale 2017-01-31 22:18:08
mbw: Rather than including constructors for each of the primitive effects, we could instead parameterise our monad over a functor which would then have cases for each of the effects
lyxia 2017-01-31 22:18:42
runeks: with existential types, data SomeC where SomeC :: C a => a -> SomeC
phadej 2017-01-31 22:18:44
reactormonk: withExceptT
lyxia 2017-01-31 22:18:57
runeks: I'm using GADT syntax there.
phadej 2017-01-31 22:19:05
reactormonk: withExceptT (:[])
_sras_ 2017-01-31 22:19:12
lyxia: A Name value that should be passed to the second function, is generated inside the first function.
reactormonk 2017-01-31 22:19:13
... I know it's there, but why doesn't https://www.haskell.org/hoogle/?hoogle=withExceptT find it
runeks 2017-01-31 22:19:41
lyxia: Is there a tutorial on this somewhere? Or just an example or two?
Cale 2017-01-31 22:19:58
data MyIOOp t = GetChar (Char -> t) | PutChar Char t
Cale 2017-01-31 22:19:58
data Free f a = Done a | Op (f (Free f a))
reactormonk 2017-01-31 22:20:06
phadej, looks good, thanks.
Cale 2017-01-31 22:20:11
Then MyIO would become Free MyIOOp
phadej 2017-01-31 22:20:19
reactormonk: because that haskell.org's hoogle have small database, and without transformers it seems
phadej 2017-01-31 22:20:33
or with very old transformers
phadej 2017-01-31 22:20:34
...
Cale 2017-01-31 22:20:34
and the nice thing about that is that we can write a Monad instance etc. for Free f generically and reuse it
reactormonk 2017-01-31 22:20:37
phadej, is there a bigger one around?
lyxia 2017-01-31 22:20:45
runeks: https://wiki.haskell.org/Existential_type perhaps
Cale 2017-01-31 22:20:47
as well as a bunch of standard shapes for interpreters
phadej 2017-01-31 22:21:00
reactormonk: I use the one at https://www.stackage.org/
phadej 2017-01-31 22:21:09
it's big enough :)
Cale 2017-01-31 22:21:17
https://hackage.haskell.org/package/free-4.12.4/docs/Control-Monad-Free.html -- here's a version of that
reactormonk 2017-01-31 22:21:20
works nicely.
lyxia 2017-01-31 22:21:35
_sras_: did you try addTopDecls, I don't know whether that works. http://hackage.haskell.org/package/template-haskell-2.11.1.0/docs/Language-Haskell-TH-Syntax.html#v:addTopDecls
Cale 2017-01-31 22:22:21
mbw: It might be confusing for a bit, but there's not really all that much to it in the end -- it's just yet another way of deferring the choice of how we implement a bunch of operations.
_sras_ 2017-01-31 22:23:07
lyxia: I just did. I tried changing the return value of the first function to Q () from Q [Dec]. But I got the error "Couldn't match type '()' with '[Language.Haskell.TH.Syntax.Dec]'"...
thatguy 2017-01-31 22:24:02
why is the instance Num a => here needed: http://lpaste.net/351860 ? Isn't Sum bound to be of numerator class since it is defined that way?
mbw 2017-01-31 22:24:05
I'm confused alright :)
runeks 2017-01-31 22:24:51
lyxia: That was just what I needed. Thanks!
mbw 2017-01-31 22:25:17
But that's mainly because I haven't really learned that much about free monads (or other free constructions in general) yet.
mbw 2017-01-31 22:25:24
So it's to be expected at this point.
lyxia 2017-01-31 22:26:20
_sras_: I meant to use it so that you could call A in the same Q computation as B, with the hope that reify sees the newly added declarations.
lyxia 2017-01-31 22:27:25
_sras_: but if your A computation returns a name, where is it stored? you said it has type Q [Dec]
lyxia 2017-01-31 22:28:19
_sras_: Can you perhaps describe your problem more concretely
_sras_ 2017-01-31 22:28:21
A computation does not return it. It calls the B, passing the Name and appends the returned [Dec] to the final result....
mbw 2017-01-31 22:29:05
Cale: Are free monads more of an academic kind of thing, or is there some practical intuition behind it that makes it a real-world design tool?
thatguy 2017-01-31 22:29:53
wait if I have newtype Book xyz = Book xyz deriving (A, B) does it mean haskell is guaranteed to derive instances of for class A and B for absolutely any type xyz?
mbw 2017-01-31 22:30:13
Sorry if I'm terribly naive...
lyxia 2017-01-31 22:30:48
_sras_: Why can't the name be regenerated in another splice
thatguy 2017-01-31 22:31:06
so I can think of my own type X which has nothing to do with numbers at all and then instanciate Book A, will Book A be somehow of Num type if I told haskell to arange that?
lyxia 2017-01-31 22:32:28
thatguy: no it derives an instance that depends on instances for xyz. instance A xyz => A (Book xyz)
kritzcreek 2017-01-31 22:32:49
is there a simple Zippers package? ekmett's zippers is way overkill for what I need, I didn't find anything in stackage. Preferrably something monomorphic to list
Axman6 2017-01-31 22:32:57
thatguy: it will probably derive instance A xyz => A (Book xyz)
_sras_ 2017-01-31 22:33:02
lyxia: Another splice mean? Another TH function? Will it make a difference?
ongy 2017-01-31 22:34:13
lyxia: does it make it A xyz => A (Book xyz) or does it complain if there's no instance A xyz?
Cale 2017-01-31 22:34:51
mbw: You can use it in the real world, though perhaps that particular formulation isn't the most popular way to go about this in a practical setting.
lyxia 2017-01-31 22:35:40
_sras_: a splice is one "$(...)". What I understood about your problem is that you are trying to call A and B in the same splice $(... A ... >>= ... B ...), but then B can't use reify to inspect the declarations made by A.
_sras_ 2017-01-31 22:36:13
lyxia: Yes.
Cale 2017-01-31 22:36:18
mbw: Often you'd like to avoid some of this interpretive overhead, but in any case, there are a lot of practical uses for having an abstract language whose operations can be implemented in a variety of different ways later.
thatguy 2017-01-31 22:36:47
hmm I am sorry I don't really understand it. If I have say data Graph t = ..some abstract graph-like strucutre, weird thing.. deriving Num and then have a g :: Graph String, then what will Haskell do to derive a Num type for g?
Axman6 2017-01-31 22:37:04
no
_sras_ 2017-01-31 22:37:14
lyxia: I see what you are saying. That is definitly an option....
Axman6 2017-01-31 22:37:16
you can't arbitrarilly derive instances
mbw 2017-01-31 22:37:27
I guess it's like talking about vectors without referring to a concrete representation, right?
Cale 2017-01-31 22:37:45
mbw: sure
lyxia 2017-01-31 22:37:53
_sras_: My first solution is to use addTopDecls on the result of A before calling B, with the hope that it makes the declarations visible to subsequent calls to reify, I don't know whether that's the case.
Axman6 2017-01-31 22:37:58
newtypes are slightly different, because it is always true that if a has an instance of class C, then it's trivial to make an equivalent instance for a newtyped a
thatguy 2017-01-31 22:38:27
yes
lyxia 2017-01-31 22:38:56
_sras_: My second solution is to call A and B in separate splices $(... A ...), $(... B ...), but you mentionned the call to B depends on data that are in the first splice.
thatguy 2017-01-31 22:39:00
Axman6, so if my structure is just too bad for haskell to get it then data Graph t = .... deriving Num will give me error at compile time?
Axman6 2017-01-31 22:39:10
yes
thatguy 2017-01-31 22:39:23
Axman6, ah well that explains it
Axman6 2017-01-31 22:39:26
the situations where classes can be derived are quire limited
lyxia 2017-01-31 22:39:30
_sras_: You can work around this if you could generate that data again from scratch in the second splice.
Axman6 2017-01-31 22:39:32
quite*
thatguy 2017-01-31 22:39:40
ah ok
thatguy 2017-01-31 22:39:40
Axman6, thanks for taking time and explaining
reactormonk 2017-01-31 22:40:36
How does the let foo bar baz = syntax work?
thatguy 2017-01-31 22:41:22
but I still have another question now. What does this then mean: newtype Sum a = Sum a deriving (Eq, Ord, Num, Show). Can I now make any type which instanciates Eq, Ord, Num, Show also to a Sum type?
thatguy 2017-01-31 22:41:31
or could I also make a string to a Sum type?
lyxia 2017-01-31 22:42:59
reactormonk: this defines a function foo with parameters bar and baz.
reactormonk 2017-01-31 22:43:16
lyxia, aah, now it makes sense. thanks.
mbw 2017-01-31 22:43:57
Cale: Though it's probably not that hard, what questions would I have to ask myself to come up with the concrete representation of GetChar and PutChar you gave? It's kind of embarrassing, but I'm not really sure I understand it yet.
mbw 2017-01-31 22:45:00
Maybe I am overthinking this