mub has quit [Killed (Sigyn (Spam is off topic on freenode.))]
neatonk has quit [Remote host closed the connection]
neatonk has joined #ocaml
ziyourenxiang has quit [Ping timeout: 264 seconds]
cbot has joined #ocaml
<adrien>
hannes: thanks for taking care of ppx_sexp_conv :)
<adrien>
(well, now I still have x509 that wants an old version of it but I know things are done in steps :) )
ben__ has joined #ocaml
benzrf has joined #ocaml
<benzrf>
hey!
<benzrf>
are there any particularly good free online resources for learning ocaml?
<benzrf>
particularly something with interesting exercises
envjj has joined #ocaml
<dmbaturin>
benzrf: There's a MOOC.
malina has quit [Ping timeout: 240 seconds]
<benzrf>
is it any good?
<benzrf>
i have extensive haskell experience, btw, if you know of anything targeting ppl who already know all about currying, hofs, parametric polymorphism, ADTs, etc
<benzrf>
are operators like +. in ocaml just identifiers made of symbols like in haskell, or is there a fixed set
envjj has quit [Ping timeout: 260 seconds]
<dmbaturin>
benzrf: Just identifiers, but the first symbol defines operator precedence.
<dmbaturin>
And infixness.
<benzrf>
oh, that's interseting
<dmbaturin>
E.g. all prefix operators should start with ~, as in let (~++) x = x + 1
<steenuil>
well no, they're not identifiers, you can't define let (compose) x y and then use it like `x compose y`
<dmbaturin>
x `compose` y ? :)
<steenuil>
though there's a few functions that are defined like that in the standard library
<benzrf>
well, obviously they dont parse the same as alphanumeric identifiers, but i meant just semantically
<benzrf>
ooh, does ocaml have arbitrary mixfix
<dmbaturin>
No, for better or worse.
<benzrf>
just prefix and binary?
<dmbaturin>
Yes. And all infix/prefix operators must be composed from a fixed set of characters.
<benzrf>
right
<dmbaturin>
I don't remember exact set offhand, the docs have it though.
<benzrf>
something similar to haskell i expect
envjj has joined #ocaml
<dmbaturin>
That "fist character defines arity and precedence" seems like a good compromise to me. User-defined precedence and infixness has _interesting_ interaction with modules.
<benzrf>
oh that makes sense
<benzrf>
modules are one of the biggest things that i kinda wish haskell had
<benzrf>
well
<benzrf>
more like module functors
<dmbaturin>
Yeah, that's why I'm here. :)
<benzrf>
i guess that's the main thing that makes modules useful tho
<benzrf>
ocaml doesnt have typeclasses, right?
<steenuil>
nope
<steenuil>
infix operators are not used that much in OCaml compared to Haskell, anyway
<dmbaturin>
No, it doesn't have ad hoc polymorphism at all, which allows it to have decidable type inference.
<dmbaturin>
There was some work on modular implicits, I haven't checked its status in a while.
<benzrf>
i'm pretty sure haskell's type inference is decidable (unless you're using a bunch of ghc extensions)
<steenuil>
that's years away
<benzrf>
(which, i mean, everyone does all the time, so :> )
<Drup>
well, similar to haskell ... but only the ascii ones
<Drup>
no utf8 operators
<dmbaturin>
Most of the time you can achieve the same effect with "functors" (modules parameterized by other modules).
<Drup>
And that's a good thing.
<benzrf>
yeah
<benzrf>
but i got the impression that module functors are a bit less ergonomic for a lot of simple use cases
<Drup>
benzrf: Haskell98's inference is decidable, yes
<Drup>
GHC Haskell's inference certainly isn't :)
* benzrf
enables -XKitchenSink
<steenuil>
benzrf yes, but using ppx like deriving makes it bearable
<benzrf>
what's ppx?
<dmbaturin>
A mechanism for syntax extensions.
<benzrf>
oh i was about to say "if your language doesnt even have gadts what good is it" tongue-in-cheekly but i guess ocaml does have gadts according to google?? nice
<Drup>
it plays a similar role to template haskell, but is midly sane
<dmbaturin>
Yes, it does.
<Armael>
I'm not sure I see the connection between ppx and typeclasses
<Drup>
OCaml has GADT but no type families and no HKT
<dmbaturin>
benzrf: Not long ago I replaced a serious chunk of Perl with OCaml. Which means yes, my legacy code is fully embracing the dumpster in a passionate way. :)
<benzrf>
companion_cube: C, because your code is already incorrect anyway, so it doesn't matter
<Drup>
It's a shit langage, but I have the intense feeling of being a wizard
<companion_cube>
:D
<cjd>
I use nThen for managing async code
<benzrf>
cjd: rewrite cjdns in rust (:
<companion_cube>
there's been a trend of crazy PL projects (including quines that go through 12 langs or so)
<cjd>
I can make it quazi-synchronous but I can still choose to do IO things concurrently
<Drup>
companion_cube: 100*
<companion_cube>
I believe Drup has a shot if he writes a Tex JIT
<cjd>
benzrf: lol
<cjd>
rust is trash
<companion_cube>
noes :(
<benzrf>
somehow i anticipated you saying that 🤔
<benzrf>
i don't even know how
<cjd>
I tried it out a few days ago
<companion_cube>
cjd: why do you say that??
<cjd>
Tried to implement like a little event loop and a setTimeout() function with MIO
<dmbaturin>
Anyone knows of a good howto on cross-compiling OCaml?
<cjd>
I'm convinced that the borrow checker was invented by some mediocher programming teacher who never wrote anything in his life but Java and never saw a callback
<companion_cube>
yeah, i just said "12" to be sure not ot overestimate from my poor memory
<cjd>
It's just completely unusable
<companion_cube>
the borrow checker is written by people with a large experience in C++ and ML…
<cjd>
ok, so sadists
<companion_cube>
but maybe starting out with mio was a bit ambitious?!
<benzrf>
cjd: u sound like someone complaining about how ocaml doesnt have mutable variables
<cjd>
it has ref()
<cjd>
what do I care about mutable vars
<companion_cube>
not the same thing tho
<benzrf>
that's quite different
<steenuil>
I really miss the borrow checker when I'm writing C
<benzrf>
all the coolest language features are things that take learning
<benzrf>
tbh
<dmbaturin>
ref is not so secretly a record with a single mutable field.
<cjd>
yeah, same as editors
<dmbaturin>
Curiously, in SML ref is a primitive.
<benzrf>
ooh, have you seen Pony
<cjd>
best ones take years of study to understand (vim, emacs, etc)
<cjd>
that's because they're religions
<Drup>
cjd: rules of thumb to avoid being considered an asshole among programmers: don't trash something by demonstrating you don't understand it :)
<companion_cube>
(in particular, don't ever thrash type theorists here)
<benzrf>
anyway if you're fighting the borrow checker that means that your code is wrong
<benzrf>
hth :)
<companion_cube>
(or people or languages, such as rust, that have a demonstrable taste in type theory)
<cjd>
Oops, guess I already screwed that one
<Armael>
well, tracking ownership in callback-y code sounds definitely tricky
<benzrf>
actually i have a better analogy
<benzrf>
you sound like someone complaining that ocaml doesn't have implicit nulls
<Drup>
(also, criticize the things, not the people who makes them)
<cjd>
ut oh, I'm probably about to get kicked for being rude
<Drup>
Lol, no
<companion_cube>
nah, we mostly kick spammers
<companion_cube>
you're not a spammer, are you? :p
<Drup>
I've done much worse anyway :>
<cjd>
I understand what the borrow checker is trying to do, thing is that I want to write code quickly and async
<steenuil>
Hey, I thought you guys might be interested in more spam
<cjd>
lol
<companion_cube>
cjd: the big mistake was critizing rust's design, instead of thrashing Go like most civilized folks do
<benzrf>
lololol
<cjd>
Go ?
<benzrf>
i mentioned Pony up above
<benzrf>
i was reading its docs the other day
<cjd>
What's go ?
<companion_cube>
golang?
<benzrf>
and it strikes me as being the version of Go that's not designed by people stuck in the 80s
<companion_cube>
have you heard of it? :D
<cjd>
oh, yeah, google thing with no macros
<cjd>
right
<benzrf>
companion_cube: shhhh don't corrupt his innocence
<steenuil>
you mean the google thing with lolnogenerics
<cjd>
or macros
<Drup>
That's a bit too easy nowadays :/
<steenuil>
I think you technically could use the CPP if you really wanted
<cjd>
I was fooling around with it back when it was new, oh man, I was about to throw the CPP in front of it
<benzrf>
steenuil: don't u know that generics are for high-falutin theorists with no practical concerns /s
<companion_cube>
what's the business use case of generics, heh
<benzrf>
real programmers stick everything in top types
<cjd>
but really, I exercised my fingers plenty doing Java back in the bad old days
<companion_cube>
Drup: abstract types are nice, but sometimes I think I'd trade them for traits, given how clean rust's stdlib is
<companion_cube>
it's really beautifully done, at least the collections part
<Drup>
This is such an armchair debate
<cjd>
Actually I would totally code rust if you could just say #[GARBAGE_COLLECTED and then write all your code in there ]
<Drup>
like, that's a trade that doesn't make the slightest sense
<cjd>
and have no borrow checker and no silly mut rules
<Drup>
cjd: so, OCaml ?
<cjd>
ocaml with macro_rules!()
<companion_cube>
ah right, abtract types are an obstacle to HKT, not traits
<companion_cube>
my bad
<cjd>
and threads
ben__ has quit [Remote host closed the connection]
<companion_cube>
still, I'm not sure we'll ever have implicits
<Drup>
companion_cube: ok, yeah, that ones is less armchair-y
<companion_cube>
right right
<benzrf>
cjd: just use Arc everywhere if you're married to mutable shared state
<companion_cube>
I just mean I'd be ready to sacrifice quite a lot to get traits, sometimes :/
<benzrf>
a.k.a. the devil
<benzrf>
also, don't do that
<cjd>
benzrf: doesn't work, because I usually don't have more than 1 thread, but it's reentrent
<cjd>
It's like always reentrent, because that's how async works
<benzrf>
what does that have to do with anything
<cjd>
Arc is reentrent ?
<benzrf>
wait, what do you mean here by reentrant
<Drup>
companion_cube: in any cases, if you remove abstract types from a language like OCaml, the langage collapses
<Drup>
nothing make sense anymore
<Drup>
you might as well use scala
<benzrf>
oh, a lock
<benzrf>
not of a piece of code
<cjd>
oh sorry, I was using Rc<Mutex<>>
<cjd>
and I found out quick that Mutex is not reentrent
<benzrf>
>mutexes
<cjd>
and my code totally is
<cjd>
and ReentrentMutex doesn't provide a mut (I think they wanted to troll)
<companion_cube>
Drup: well I'd be really interested in something like OCaml, but where functors are replaced by traits
<companion_cube>
(and maybe with better perf, at least with more specialisation)
<Drup>
companion_cube: that's why I really dislike those "I would sacrifice language feature X for language feature Y" when talking about an *existing* language
<benzrf>
cjd: wait, if you only have one thread, why are you even using a lock
<cjd>
because I want to get a mut
<Drup>
If you are designing a new language, those question makes sense, but then you are designing a whole language, with parts that fit together
<companion_cube>
why not? I can start from ML if you want, it's more of a general spec than an actual languae 0:-)
<cjd>
I'm passing around an object all over and I need a mut of it
<benzrf>
i don't think you need a lock for mut
<benzrf>
don't you just need rc
<Drup>
but if you are talking about an existing language, like OCaml, you can't just throw away features and replace them. Programming language features are not modular like that :p
<benzrf>
oh wait no i forgot what arc does
<benzrf>
i havent actually written all that much rust :>
<steenuil>
yeach, you can't just throw a bunch of features at the wall and call it a language
<steenuil>
that's called python
<Drup>
(well, they can be, but then your language is shit because everything is bolted on, and you don't account for the interactions)
<companion_cube>
Drup: right, but there still are some general lines
<cjd>
doesn't work
<Drup>
(or, said differently, the C++ school of language design)
<cjd>
I can't find any way for an object to have a mut access to itself
<companion_cube>
I don't mean to take OCaml and remove/add exactly what I said, but more orienting the design along slightly different lines
<cjd>
apparently you can only have 1 mut pointer to an object
<benzrf>
yes, that's kind of the point
<Drup>
companion_cube: there are, but most people who don't actually study them don't know them very well
<benzrf>
that's the entire point of the borrow checker
<companion_cube>
I'm no expert, but please don't tell me I don't know OCaml very well -_-
<Drup>
companion_cube: Ok, tell me, do you understand the reason for the following statement "The value restriction doesn't apply to contra variance because float arrays are specialized"
<companion_cube>
if you prefer, my claim could be altered to be more like "take SML, but with OCaml's pattern matching, no functors, and rust' traits + specialisation" ;-)
<Drup>
companion_cube: yeah, that langage probably doesn't have separate compilation out of the box, are you ready to sacrifice that as well ? :3
<cjd>
It seems that you only get 1 mut pointer to an object and that's a rule which you positively cannot break
<companion_cube>
it'd have as much separate compilation as flambda, right? but yeah, I would
<Drup>
Well, you can always try to use mlton
<Drup>
I doubt you would last a long time in practice :D
<companion_cube>
regarding the previous thing, I'm not exactly sure, I must say, but I suppose it's because `'a array -> foo` still has to preform runtime checks on the arrays because of bloody float arrays
<companion_cube>
well rust seems to survive…
<companion_cube>
mlton does whole program optimization, I didn';t argue for sth that strong
<benzrf>
cjd: under what circumstances are you taking mut out of the cell and not letting it go before the next time your code needs a mut
<Drup>
anyway, this is armchair design/circle jerking and I have to go :)
<companion_cube>
I only ever need variance for private types :/
<Drup>
But the tradeoffs in language designs are really subtle things, and just slapping "I want those things" sometimes doesn't really get you all that you really want. :)
<companion_cube>
private aliases*
<benzrf>
cjd: so you're complainign about a language stopping you from writing bad code
<cjd>
it's the part with the event loop that the borrow checker is being a pain
<benzrf>
this code is nofun
<cjd>
I agree
<cjd>
in javascript it's function () { before(); func.apply(func, arguments); after(); }
<cjd>
but you know, not all languages can be that good
<benzrf>
why not
<benzrf>
like
<benzrf>
use tuple types
<companion_cube>
to people used to strong typing, this reads "function() { if yolo() { crash; }; before(); if yolo() { crash; } func.apply(func, whatever()); after() }
<companion_cube>
"
<companion_cube>
(more seriously, yeah, things are easier with a GC)
<cjd>
uhh, actually that little code snippet can't crash
<companion_cube>
(it'd probably look relatively similar in OCaml with libuv)
<cjd>
oh, unless func is undefined
<companion_cube>
or before() is not defined?
<cjd>
or before or after, but c'mon, you define those things
<companion_cube>
I do when the type checker tells me so :-)
<cjd>
yeah, I use flow typechecker which helps a bit
<benzrf>
cjd: what is this World
<cjd>
benzrf: The idea is it's an object with the functions for interacting with the system
<benzrf>
oh wait
<benzrf>
ok
<cjd>
so World.println() or World.filesystem.open() etc
<benzrf>
srsly tho why not just use tuple types with these callback types
<cjd>
because the code which calls you might not provide 1 arg
<cjd>
anyway I only got as far as writing set_timeout() and set_interval() before I gave up
<benzrf>
why wouldn't it...?
<cjd>
because it wasn't written by me
<cjd>
I mean to support function wrappers for any callback function which is passed to any async function
<benzrf>
but you're using a type declared by you, so code not written by you can't be calling it
<benzrf>
but what i'm saying is that you're building these new Callback4 or whatever types
<benzrf>
so library.functionIDontControl cannot take one as an argument directly
<cjd>
apparently it can
<cjd>
oh, wait, no, nvm
<benzrf>
lol
<cjd>
yeaaaah, that works in ocaml, rust not so much
<benzrf>
well, i guess you can implement the FnWhatever trait
<cjd>
ehh, no, more complicated than that
<cjd>
You apparently can't pass a function directly and have it's scope go with it
<benzrf>
what do you mean
<cjd>
Callback is an object which contains a method cb
<cjd>
if I pass the method cb, no more &this
<cjd>
Rust has a lambda type, but that's a bunch of dark magic which results in every lambda having a unique type
<companion_cube>
which helps specializing most higher order functions, and eventually inlining them
<cjd>
I could probably do a bunch more dark magic to emulate lambda thingy in order to get the scope again, but that's an ugly rabbithole
<benzrf>
you want the FnWhatever trait is what you want
<cjd>
++
Autolycus has joined #ocaml
<cjd>
in any case, there's a lot of functions which need to access the event loop directly, and then need to call out to the "userland" and then the userland might register another callback which also needs to access the event loop directly
<cjd>
so it's gonna be a mess of deadlocks and panics, despite 1 thread
<cjd>
handle_timeout() (when the setTimeout completes) needs to deregister it (mut event loop) then it needs to call userland and userland might set_timeout() again (mut event loop)
<cjd>
#mess
cbot_ has joined #ocaml
<benzrf>
12:39 <benzrf> cjd: under what circumstances are you taking mut out of the cell and not letting it go before the next time your code needs a mut
<cjd>
yeah, it took me 15 minutes and re-reading my code to remember :)
cbot has quit [Ping timeout: 264 seconds]
<benzrf>
you didnt answer tho i dont think
<cjd>
So in the event loop, the handler for when there's data on the socket or there's a timeout which has timed out, needs to mutate the event loop
<benzrf>
ok, but why not take out a refmut, use it, then let go of it
<benzrf>
while continuing to hold the cell
<benzrf>
like, i think that's the point of cells
<cjd>
then it needs to call the functions which were registered, and they might call the loop
<cjd>
because the functions are in the loop
<benzrf>
so give them the cell
<benzrf>
or rather, a copy of it
<cjd>
right, so you have a read only copy of the loop and a writable copy of the loop and you read the function out of the read-only
<cjd>
maybe I will finish my experiment
<benzrf>
i dont know what you mean
<cjd>
the function is in the loop, so if I have a mut access on the loop and I try to pick the function out, to call it, I'm gonna get an error
<benzrf>
isnt that what methods are for
<benzrf>
oh wait
<cjd>
It should work if I have 2 pointers to the loop
<benzrf>
you're not supposed to have that, tho
<cjd>
mut and non-mut ?
<cjd>
hmm
* benzrf
tests a thing
<cjd>
so I think if I can encapsulate everything that touches mut in the loop into methods which don't call anything outside of themselves, it does work
<cjd>
I think
<cjd>
Clearly I'm not going to write like this every day
<cjd>
life is way too short to shovel that kind of dirt every day
<benzrf>
i don't think you need to
<cjd>
bbl food
olle has joined #ocaml
envjj has quit [Ping timeout: 244 seconds]
unyu has quit [Quit: Reboot.]
cbot has joined #ocaml
unyu has joined #ocaml
cbot_ has quit [Ping timeout: 240 seconds]
FreeBirdLjj has quit [Remote host closed the connection]
FreeBirdLjj has joined #ocaml
theglass has quit [Ping timeout: 276 seconds]
RyZum has joined #ocaml
<RyZum>
Hey, I thought you guys might be interested in this blog by freenode staff member Bryan 'kloeri' Ostergaard https://bryanostergaard.com/
<RyZum>
or maybe this blog by freenode staff member Matthew 'mst' Trout https://MattSTrout.com/
RyZum has quit [Killed (Sigyn (Spam is off topic on freenode.))]
cbot has left #ocaml ["Leaving"]
<cjd>
I wonder what that script is
<cjd>
tried curling it, earlier it resovled but the connection hung
<cjd>
now the domain isn't resolving
<benzrf>
l
Jesin has joined #ocaml
<benzrf>
Armael: >Real World OCaml is aimed at programmers who have some experience with conventional programming languages, but not specifically with statically typed functional programming. Depending on your background, many of the concepts we cover will be new, including traditional functional-programming techniques like higher-order functions and immutable data types, as well as aspects of OCaml's powerful type and
<benzrf>
Armael: you recommended it to me after i said "i know a lot of typed fp stuff already from haskell"
<Armael>
well
<Armael>
the other good ocaml book I know is for beginners
<Armael>
so
<benzrf>
heh
<Armael>
I think you should be fine with RWO, just skip ahead if you already know some of the stuff
<Armael>
unfortunatly I don't think we have a "ocaml for haskellers" tutorial
<benzrf>
o well
envjj has joined #ocaml
ben__ has joined #ocaml
jao has joined #ocaml
Autolycus has left #ocaml [#ocaml]
ben__ has quit [Ping timeout: 265 seconds]
ben__ has joined #ocaml
<companion_cube>
the book is just "check your laziness at the door"
FreeBirdLjj has quit [Remote host closed the connection]
ZirconiumX has quit [Quit: Love you all~]
kleimkuhler has joined #ocaml
ZirconiumX has joined #ocaml
pierpal has quit [Quit: Poof]
pierpal has joined #ocaml
jack5638 has quit [Ping timeout: 260 seconds]
jack5638 has joined #ocaml
<cjd>
You know what would be great, is if I could annotate a function with X and then I can annotate another function cannot_ever_call(X)
<cjd>
so then any possible call chain by which one function can end up calling through to the other is a compiler error
<cjd>
Java had something like that with their explicit checked exceptions which you had to handle or throw, of course you had to type a lot, but everything is Java is typing
<cjd>
Personally I wish all blocking functions were annotated, so that I'm not going to either A. call them from an event loop and freeze the world or B. accidently call them while holding a super lock and then freeze the world
kleimkuhler has quit [Quit: kleimkuhler]
<bartholin>
let annot = ref false;;
envjj has quit [Ping timeout: 244 seconds]
<bartholin>
let f x = if !annot then failwith "gotcha" else x*x;;
<bartholin>
let g x = 2 * f x;;
<bartholin>
let h x = annot := true; try let y = g x in annot := false; y with | e -> annot := false; raise e;;
<bartholin>
# h 4;;
<bartholin>
Exception: Failure "gotcha".
<bartholin>
# g 4;;
<bartholin>
- : int = 32
<bartholin>
Maybe too hack-ish
pierpa has joined #ocaml
<cjd>
hmm
<cjd>
problem is it's runtime
<bartholin>
oh
<bartholin>
right
<ZirconiumX>
Isn't that essentially asking the compiler to solve a potentially unbounded problem?
<cjd>
wouldn't think so
<ZirconiumX>
Imagine function A is recursive with a depth parameter, and calls B when the parameter is zero, which calls C
<ZirconiumX>
And A is annotated can't call C
<cjd>
If function A has a call to function B in it, consider that it can potentially call it
<cjd>
I'm ok with that
<cjd>
even if it's in an if(false)
<ZirconiumX>
What's your use case for this, anyway?
<cjd>
annotate the database calls with cant_call_this_while_holding_a_lock
kakadu has joined #ocaml
<ZirconiumX>
So what you're actually looking for is thread safety
<bartholin>
use a monad
<cjd>
or cant_call_this_in_the_event_loop_thread
<ZirconiumX>
Which can be solved with cooperative threading
<ZirconiumX>
Or a monad
envjj has joined #ocaml
<cjd>
I don't really know enough fp to understand how a monad helps
<bartholin>
create a "thiscanbeblocking" monad, so you annotate that a function can be blocking
<cjd>
ok
envjj has quit [Ping timeout: 264 seconds]
envjj has joined #ocaml
loli has quit [Quit: WeeChat 2.2]
loli has joined #ocaml
envjj has quit [Ping timeout: 240 seconds]
zolk3ri has quit [Remote host closed the connection]
dtornabene has joined #ocaml
envjj has joined #ocaml
BitPuffin has quit [Remote host closed the connection]
jack5638 has quit [Ping timeout: 240 seconds]
jack5638 has joined #ocaml
kleimkuhler has joined #ocaml
kakadu has quit [Ping timeout: 256 seconds]
kleimkuhler has quit [Quit: kleimkuhler]
letoh has quit [Ping timeout: 248 seconds]
theglass has quit [Ping timeout: 256 seconds]
kleimkuhler has joined #ocaml
theglass has joined #ocaml
theglass has quit [Changing host]
theglass has joined #ocaml
theglass has quit [Ping timeout: 256 seconds]
envjj has quit [Ping timeout: 268 seconds]
theglass has joined #ocaml
theglass is now known as Guest18142
|jbrown| has joined #ocaml
|jbrown| has quit [Client Quit]
steenuil has quit [Read error: Connection reset by peer]
steenuil has joined #ocaml
jao has quit [Ping timeout: 240 seconds]
loli has quit [Quit: WeeChat 2.2]
loli has joined #ocaml
WSPR0 has joined #ocaml
WSPR0 has quit [Remote host closed the connection]
tane has quit [Quit: Leaving]
Guest18142 has quit [Ping timeout: 265 seconds]
envjj has joined #ocaml
Shnaw7 has joined #ocaml
theglass has joined #ocaml
theglass has quit [Changing host]
theglass has joined #ocaml
Shnaw7 has quit [Remote host closed the connection]