--- Log opened Fri Jul 25 00:00:57 2008 |
00:25 | <@McMartin> | http://steve.yegge.googlepages.com/singleton-considered-stupid is also fun |
00:30 | <@ToxicFrog> | What's on pp.243-256 that makes them both coma-inducing and the most important part of the book? |
00:30 | <@McMartin> | I imagine that's the spec for Singleton. |
00:31 | <@McMartin> | We don't seem to have the book here in lab. I'll have to check later |
00:32 | <@ToxicFrog> | I don't think it's singleton, as he says: "I don't hold it against you, of course. Nobody does a very good job of explaining that one. It takes a very, very long time before people can appreciate it, and many people never do. If YOU do, I have openings on my team." |
00:33 | <@ToxicFrog> | And describes those pages as "coma-inducing" but singleton as "warm and fuzzy" |
00:33 | <@Vornicus> | he's being sarcastic. |
00:34 | <@ToxicFrog> | In that case, that doesn't come across at all. |
00:34 | <@McMartin> | If TF's reading is right, then it's probably MVC. |
00:35 | <@McMartin> | Which indeed nobody on Earth can explain right. |
00:36 | <@McMartin> | He's, imo, wrong about Visitors, though. |
00:36 | <@McMartin> | Visitor isn't map/accumulate, though you can use it to do that |
00:36 | <@McMartin> | Visitor is multimethods. |
00:37 | | Corra [~Corra@Nightstar-25039.hr.hr.cox.net] has joined #code |
00:37 | < Corra> | hello. |
00:37 | <@ToxicFrog> | I think I understand MVC, but I'm not sure. |
00:37 | <@ToxicFrog> | 'evening, Corra. |
00:37 | < Corra> | Thanks. You too, and long time no see. |
00:38 | | * Corra pokes Toxic. |
00:38 | <@McMartin> | The division between V and C is fuzzy, as is where in the execution stack the C lives. |
00:38 | <@ToxicFrog> | The way I generally structure it is that each gesture in the V maps to exactly one signal to the C, which then performs actual manipulations of the M. |
00:39 | | * ToxicFrog dies |
00:39 | <@McMartin> | ? |
00:39 | <@McMartin> | What is this, #nethack? |
00:41 | <@ToxicFrog> | ? |
00:42 | <@McMartin> | Sudden death in #code is rare~ |
00:47 | <@ToxicFrog> | I was poked! |
00:49 | < Corra> | hehe... |
00:49 | < Corra> | he usually only meeps. ^_^; |
00:50 | < Corra> | So.. anyone know anything about robotics? |
00:50 | <@ToxicFrog> | McMartin: that was a fun article, and reminds me that I really should read DP at some point. |
00:50 | <@Shoukanjuu> | What about robotics? |
00:51 | <@ToxicFrog> | I have some familiarity with microcontrollers, servos, and the like, but not robotics per se. |
00:51 | <@McMartin> | TF: Some degree of skepticism is appropriate. |
00:51 | <@McMartin> | It's mostly useful for the language features. |
00:51 | < Corra> | well... resources... like websites to investigates, methodologies... etc. |
00:51 | <@ToxicFrog> | McMartin: quite. But it would be nice to at least know what all the terms mean. |
00:51 | <@Shoukanjuu> | Servos for R/C planes my dad used to make...>_> |
00:53 | <@ToxicFrog> | Can't help you there, I'm afraid |
00:54 | < Corra> | Ok, not a problem. |
01:24 | < Doctor_Nick> | :D |
01:41 | | * ToxicFrog ponders OcaML |
01:41 | <@ToxicFrog> | I keep wanting to learn it, because it looks friendlier for common tasks (like, say, IO, or reading command line arguments...) than Haskell. |
01:41 | <@ToxicFrog> | But the complete lack of overloaded operators still freaks me out. |
01:47 | <@McMartin> | Also be warned that on a 32-bit CPU, int is 31 bits. |
01:48 | < Corra> | are only signed integers 31 bits? |
01:52 | <@McMartin> | int is always a signed type on OCaml. |
01:52 | <@McMartin> | Bit vectors are separate. |
01:52 | <@McMartin> | (There is also Int32, Int64, and nativeint) |
02:13 | <@MyCatVerbs> | ToxicFrog: Haskell's pretty friendly for IO, IMO. |
02:13 | <@MyCatVerbs> | ToxicFrog: I'm perhaps somewhat biased, though. |
02:14 | <@MyCatVerbs> | ToxicFrog: and if ML's lack of overloading puts you off, you'll probably really like Haskell's typeclass system. |
02:14 | <@MyCatVerbs> | Corra: 32-bit signed integers use two's complement representation in modern machines. |
02:15 | < Corra> | Ah. |
02:15 | <@MyCatVerbs> | Corra: they're still 32-bits wide, but the range is -2^31 to (2^31)-1. |
02:16 | <@MyCatVerbs> | Unsigned 32-bit integers, on the other hand, have range 0 to 2^32-1. Make sense? |
02:16 | <@ToxicFrog> | MyCatVerbs: Haskell is not friendly for IO; any sort of IO poisons your entire program with monads. |
02:16 | <@ToxicFrog> | The same is true of a lot of things that I don't think should be stateful, but which in Haskell apparently are, like reading argv. |
02:17 | <@MyCatVerbs> | ToxicFrog: not really in practice. |
02:17 | <@ToxicFrog> | I love Haskell's typeclass system, but in practice I can't do anything with the language |
02:17 | <@MyCatVerbs> | ToxicFrog: so unsafePerformIO getArgs. Or, more commonly, getArgs somewhere inside main and then pass the resulting string into the pure code that's doing your computations. |
02:18 | <@ToxicFrog> | Because random number generation, interaction with the user, etc all involve monads, and I can never de-monadize anything, so before long everyone has monadic types which I don't understand because no-one can explain them clearly! |
02:18 | <@ToxicFrog> | Except the resulting string is a monadic type. |
02:18 | <@MyCatVerbs> | Mmmm, no. |
02:18 | <@ToxicFrog> | How do I get it, then? |
02:18 | <@MyCatVerbs> | You call the pure code from the impure code. |
02:19 | <@ToxicFrog> | Because I was beating my head against this WRT random numbers for days last year, half the people I talked to said it couldn't be done, the other half said it could but couldn't explain it, and in the end I gave up and used lua instead. |
02:19 | <@ToxicFrog> | I call the pure code from the impure code, yes |
02:19 | <@ToxicFrog> | But I need to pass it an IO String |
02:19 | <@MyCatVerbs> | You have something like, pureComputation :: Foo -> IO Bar, right? |
02:19 | <@MyCatVerbs> | Er, better name for htat. |
02:19 | <@ToxicFrog> | Or it needs to return an IO Something |
02:19 | <@ToxicFrog> | And it's not pure anymore; it needs to worry about monadic types as well |
02:19 | <@MyCatVerbs> | You have computation :: Foo -> IO Bar, even though computation doesn't actually do anything side-effectful. |
02:20 | <@MyCatVerbs> | All because computation calls getArgs, yes? |
02:20 | <@ToxicFrog> | Yeah. |
02:21 | <@ToxicFrog> | Or, for a concrete example, updateStateVector :: StateVector -> StateVector calls the RNG, which is monadic |
02:21 | <@MyCatVerbs> | So you're calling it with a main that looks something like, say, main = in <- readFile "yarrr"; out <- computation (parseToFoo input); putStrLn (show out); |
02:21 | <@ToxicFrog> | So now everything that ever interacts with updateStateVector at any remove, IE, the entire program, needs to worry about this. |
02:21 | <@MyCatVerbs> | ToxicFrog: please let's deal with one example at a time. |
02:21 | <@MyCatVerbs> | ToxicFrog: because I myself have also written code that looks pretty much like that, and it's not an issue. |
02:21 | <@ToxicFrog> | Ok. |
02:22 | <@MyCatVerbs> | ToxicFrog: so in the first case, what you want to do is instead write pureComputation :: [String] -> Foo -> Bar. Note the lack of IO? |
02:22 | <@ToxicFrog> | Alright... |
02:23 | <@MyCatVerbs> | Then call it like, main = do { in <- readFile "foo"; args <- getArgs; putStrLn (show (pureComputation args (parseAsFoo in))); } |
02:24 | <@MyCatVerbs> | Arguably, you would be _entirely_ justified in using unsafePerformIO inside purecomputation on getArgs, since argv+argc are friggin' constants, yo. |
02:24 | <@ToxicFrog> | What's the type of getArgs? |
02:24 | <@MyCatVerbs> | getArgs is in System.Environment, and has type IO String. |
02:25 | <@ToxicFrog> | How does the call to pureComputation work, then? |
02:25 | <@MyCatVerbs> | args <- getArgs, inside a piece of do notation, executes getArgs and binds the result to args. args :: String, specifically. |
02:25 | <@ToxicFrog> | It expects [String], you're passing it IO String. |
02:25 | <@MyCatVerbs> | Whoops. getArgs :: IO [String], sorry. |
02:25 | | * MyCatVerbs facepalms. |
02:26 | <@MyCatVerbs> | Very sorry about that. Anyway, getArgs just returns argv parsed into a list. |
02:26 | <@ToxicFrog> | Ok. So the <- automatically de-monadizes it within that scope? |
02:26 | <@ToxicFrog> | So to speak? |
02:26 | <@MyCatVerbs> | <- can be read as "get the result out of". |
02:26 | <@ToxicFrog> | Ok. |
02:27 | <@ToxicFrog> | Now, how about the converse, pure code calling impure code? |
02:27 | <@MyCatVerbs> | Don't, unless you can prove (on a blackboard, no less) that the order in which the impure code is called (or even if it is called) doesn't matter. |
02:28 | <@MyCatVerbs> | There is unsafePerformIO :: IO a -> a. It's called unsafePerformIO for a reason, because it really won't interact very nicely with the rest of your program if you, say, care about what order operations are done in. :) |
02:28 | <@ToxicFrog> | Going back to the example that actually drove me insane - I have a simulator. It can basically be expressed as a tail-recursive function which calls a function State -> State which maps the current state of the simulation to the state at the next time unit. |
02:29 | <@ToxicFrog> | In the uttermost depths of this update function is a requirement for random numbers. |
02:29 | <@ToxicFrog> | The two ways I heard of doing this were: |
02:29 | <@MyCatVerbs> | Right. In this case, you want to use the State monad. |
02:30 | <@ToxicFrog> | (1) the top level gets the random numbers and passes them down the chain. Impure calling pure. This works but means lots more back and forth and everything needs an extra argument. |
02:30 | <@MyCatVerbs> | Are you okay with using a PRNG? |
02:30 | <@ToxicFrog> | (2) The bottom level gets the random numbers and pretty much the entire program is rewritten to use monadic types, because while I can go impure->pure I can't do the converse. |
02:30 | <@ToxicFrog> | State was mentioned, but never in any way I could make sense of. |
02:30 | <@ToxicFrog> | If you can explain how it actually works I would be quite grateful. |
02:31 | <@MyCatVerbs> | Right. Want some source code? :) |
02:31 | <@ToxicFrog> | Although first I should have dinner. |
02:31 | <@ToxicFrog> | Source when I get back would be lovely. |
02:31 | <@MyCatVerbs> | Because I have a (poor, because my culling method isn't fitness-proportional) GA implemented in Haskell, lying around. |
02:33 | | Attilla [~The.Attil@92.9.153.ns-3442] has quit [Ping Timeout] |
02:33 | | Attilla [~The.Attil@92.9.153.ns-3442] has joined #code |
02:33 | | mode/#code [+o Attilla] by ChanServ |
02:39 | <@MyCatVerbs> | http://www.cs.bris.ac.uk/home/rb6822/darcs/alcopizza/GeneticAlgorithm.hs - sorry that it's going to look quite scary at first. |
02:40 | <@MyCatVerbs> | http://www.cs.bris.ac.uk/home/rb6822/GeneticAlgorithm.html <-- here's the same, plus syntax-highlighting, courtesy of HsColour. |
02:43 | <@MyCatVerbs> | What I'm doing is explicitly passing around the PRNG's state, right? The (RandomGen gen) => gen, in there, is a type variable denoting "anything which denotes a random number generator's state". |
02:44 | <@MyCatVerbs> | Hrmn, damn, I need to go to sleep. |
02:44 | < Corra> | nighgt |
02:44 | <@MyCatVerbs> | Also, I need to beautify this code somewhat. It kinda makes me go "squick" right now. Heh. |
02:45 | <@MyCatVerbs> | Thanks. |
02:45 | <@MyCatVerbs> | ToxicFrog: please hit me up tomorrow? I'd love to go through how this works with you. |
02:49 | | Thaqui [~Thaqui@Nightstar-1405.jetstream.xtra.co.nz] has joined #code |
02:49 | | mode/#code [+o Thaqui] by ChanServ |
03:12 | <@ToxicFrog> | MyCatVerbs: the creator of HsColour is clearly an alien. |
03:39 | | Attilla [~The.Attil@92.9.153.ns-3442] has quit [Ping Timeout] |
04:28 | | Corra [~Corra@Nightstar-25039.hr.hr.cox.net] has quit [Quit: God Bless] |
05:25 | | * Kazriko thinks qooxdoo looks very interesting, should try it on the ennesbot project. |
06:31 | | Vornicus is now known as Vornicus-Latens |
07:43 | | Thaqui [~Thaqui@Nightstar-1405.jetstream.xtra.co.nz] has quit [Quit: MORE constitution LESS destitution MORE pros...perity.] |
08:10 | | AnnoDomini [AnnoDomini@Nightstar-29162.neoplus.adsl.tpnet.pl] has joined #Code |
08:10 | | mode/#code [+o AnnoDomini] by ChanServ |
08:44 | | You're now known as TheWatcher |
10:41 | | Attilla [~The.Attil@92.9.153.ns-3442] has joined #code |
10:41 | | mode/#code [+o Attilla] by ChanServ |
11:23 | | AnnoDomini [AnnoDomini@Nightstar-29162.neoplus.adsl.tpnet.pl] has quit [Ping Timeout] |
11:30 | | AnnoDomini [AnnoDomini@Nightstar-29268.neoplus.adsl.tpnet.pl] has joined #Code |
11:30 | | mode/#code [+o AnnoDomini] by ChanServ |
15:07 | <@ToxicFrog> | qooxdoo? |
15:47 | | Vornicus-Latens is now known as Vornicus |
16:45 | <@ToxicFrog> | Hmm. |
16:45 | <@ToxicFrog> | When I look at the unmap log, I can see the FAD page in the list. |
16:46 | <@ToxicFrog> | But when I modify free_physical_address_space to enter the debugger when passed a FAD page, it doesn't trip. |
16:57 | | AnnoDomini [AnnoDomini@Nightstar-29268.neoplus.adsl.tpnet.pl] has quit [Quit: God created man, God created woman, but Samuel Colt made them equal.] |
17:25 | <@MyCatVerbs> | ToxicFrog: ugh, yeah. Repainted with a stylesheet with less-crazy colours. http://www.cs.bris.ac.uk/home/rb6822/GeneticAlgorithm.html |
17:27 | <@MyCatVerbs> | ToxicFrog: anyway. The thing with random number generation is that you have a PRNG, from which you can pull integers. The function used on it looks something like (RandomGen gen) => gen -> (gen,Int). So a PRNG's state is something you can pull a number out of, yielding a new PRNG state. |
17:28 | <@ToxicFrog> | Right. |
17:28 | <@ToxicFrog> | What it looks like you're doing here is explicitly passing the RNG around. |
17:29 | | * MyCatVerbs nods. |
17:29 | <@MyCatVerbs> | That is exactly it. |
17:29 | <@MyCatVerbs> | That is exactly what the State monad there is for. |
17:29 | <@ToxicFrog> | Ok. Please explain State |
17:29 | <@MyCatVerbs> | State is pretty uncontroversial. |
17:30 | <@ToxicFrog> | It also looks like while you aren't passing the RNG into functions, a lot of them return it |
17:30 | <@MyCatVerbs> | (State s a) is a stateful computation, where the inner state is of type s, and the result is of type a. |
17:31 | <@ToxicFrog> | Ok |
17:32 | <@MyCatVerbs> | To unwrap it (like, maybe you want to actually use these values ^_^), you have a function called runState, which turns a (State s a) into an (s -> (a,s)) |
17:32 | <@ToxicFrog> | So (RandomGen gen, Genome g) => State gen [g] has a state of type RandomGen and a result of type list of Genome |
17:32 | <@MyCatVerbs> | Yep. |
17:32 | <@ToxicFrog> | Which makes sense for, say, the population initializer |
17:33 | <@MyCatVerbs> | So: in the State monad, you have four (interesting) operations: >>=, return, get and put. |
17:33 | <@ToxicFrog> | What do you mean by "in" here? |
17:34 | <@MyCatVerbs> | Where I'm using do notation, effectively. Where I'm building up State computations by gluing one onto another with >>=. |
17:35 | <@ToxicFrog> | Where you're using do notation...inside a function declared as returning a State? |
17:35 | <@MyCatVerbs> | Yep. do notation works for any monad, not just IO. |
17:35 | <@ToxicFrog> | That much I know. |
17:35 | <@MyCatVerbs> | It's just syntactic sugar for (>>=) and return. |
17:36 | <@MyCatVerbs> | Looking at geneticIteration first, because that's where all the little building blocks are... |
17:36 | <@ToxicFrog> | -- this pairing method doesn't even promise to not breed individuals with themselves, but I don't care |
17:36 | <@ToxicFrog> | pfft |
17:37 | <@MyCatVerbs> | Ignore the pairing method. It's the culling method that really sucks. :) |
17:37 | <@MyCatVerbs> | We have, for example, smackFitness :: Double -> State gen Double. |
17:37 | <@ToxicFrog> | ....yeah, this, um, what |
17:37 | <@ToxicFrog> | Fortunately it's not the algorithm we're interested in. |
17:37 | <@MyCatVerbs> | Aye, because it sucks. ^_^ |
17:38 | <@MyCatVerbs> | Oh, sorry, smackFitness takes a pair. But okay, we're only interested in the second part. |
17:38 | | * ToxicFrog tries to puzzle out pullRand |
17:38 | <@MyCatVerbs> | So we have smackFitness :: (a,Double) -> State gen (a,Double), right? |
17:39 | <@MyCatVerbs> | ...I was working towards that. :) |
17:39 | <@MyCatVerbs> | Okay, let's hop to *that* instead, then. |
17:39 | <@ToxicFrog> | pullRand takes a function (which takes a random number generator and returns a random number generator) and returns a State gen r |
17:39 | <@ToxicFrog> | It then, let's see here |
17:39 | <@MyCatVerbs> | Yuss. |
17:39 | <@MyCatVerbs> | gen <- get, gets ahold of the current state. |
17:40 | <@MyCatVerbs> | let (r,gen') = f gen, I'm pulling the new value and the new PRNG state. |
17:40 | <@ToxicFrog> | Uses get to extract the RNG from the State monad; passes it to this function to get an updated RNG and a random number; puts the updated RNG; and returns r |
17:40 | <@MyCatVerbs> | Yuss. |
17:41 | <@MyCatVerbs> | For example, (pullRand (randomR (0,1::Double))) has type State gen Double, and will pull a number (uniformly distributed between 0 and 1) from the RNG. |
17:41 | <@ToxicFrog> | Hmm. Ok, this means shuffle has type shuffle :: [a] -> (gen -> (r,gen)), I think |
17:41 | | * MyCatVerbs nods. |
17:42 | <@ToxicFrog> | randomR being a predefined function to extract numbers from an RNG? |
17:42 | <@MyCatVerbs> | Yes. It's one of the functions defined by the Random class. |
17:42 | <@ToxicFrog> | Alright. |
17:42 | <@ToxicFrog> | ...how do lefts and rights work? |
17:42 | <@MyCatVerbs> | For instance, there's instance Random Double defined somewhere in System.Random. |
17:43 | <@ToxicFrog> | It looks like they should be numbers, but then you zip them later |
17:43 | <@MyCatVerbs> | They're lists. |
17:43 | <@ToxicFrog> | Oh, wait |
17:43 | <@ToxicFrog> | pullRand is polymorphic |
17:43 | | * MyCatVerbs nodnod. |
17:43 | <@ToxicFrog> | f doesn't extract a number, it extracts a value |
17:43 | <@MyCatVerbs> | shuffle :: [a] -> (gen -> ([a],gen)) |
17:44 | <@ToxicFrog> | Ok |
17:44 | <@ToxicFrog> | Now that bit makes sense |
17:44 | <@MyCatVerbs> | Uhuh. Most often I'm using it with numbers, but any old value will do. |
17:44 | <@ToxicFrog> | shuffle list returns a function that when passed an RNG returns a shuffled version of list and an updated RNG. |
17:44 | <@MyCatVerbs> | Indeedely-do. |
17:44 | <@ToxicFrog> | You call it through pullRand to make sure the RNG gets updated appropriately. |
17:45 | <@MyCatVerbs> | Yup. |
17:45 | <@MyCatVerbs> | I could easily have written it as [a] -> State gen [a], and used get and put in there manually to update the RNG state, but this way is less typing. |
17:46 | <@MyCatVerbs> | (Besides, I think it's quite nice to have shuffle come out with a type that looks just the same as the standard random generation functions, don't you? ^_^) |
17:49 | <@ToxicFrog> | Oh, shuffle is your own code? |
17:49 | <@MyCatVerbs> | Yes. |
17:50 | <@MyCatVerbs> | I'll put it up now if you like. Moved it out into a seperate file because I found a use for it in some other program. |
17:51 | <@MyCatVerbs> | http://www.cs.bris.ac.uk/home/rb6822/Shuffle.html |
17:53 | <@ToxicFrog> | What's Control.Monad.ST? |
17:53 | <@MyCatVerbs> | This one's a bit odder, because I use the ST monad inside it in order to get an O(1) mutable array in pure code. The reason for the type-checker-related ranting is that, in order to prevent references to the mutable array(s) inside the ST monad from leaking into pure code (which would cause Bad Things to happen), the ST monad involves a little bit of type-level evil. |
17:54 | <@ToxicFrog> | Oh. |
17:54 | <@MyCatVerbs> | ST is the "State Transformer" mode. Basically, it's all the stuff that C programmers think of as "pure computation". |
17:54 | <@MyCatVerbs> | So inside the ST monad you can have mutable arrays and mutable references, and because it's guaranteed that nothing will leak out of the ST monad, you can call ST stuff (using runST, as you can see) from pure code. |
17:55 | <@ToxicFrog> | What's the type of blast? |
17:56 | <@MyCatVerbs> | blast :: ST (forall s. s) ([a],gen) |
17:56 | <@ToxicFrog> | Ok. |
17:56 | <@MyCatVerbs> | The s parameter in there is what stops the ST computation's state from being able to leak out. |
17:56 | <@ToxicFrog> | Looking back at the GA...all the code using the State monad ultimately gets that state from the initial call to runState in sinutest*? |
17:57 | <@MyCatVerbs> | runState :: State s a -> (s -> (a,s)) |
17:57 | <@MyCatVerbs> | runState takes a State computation and an initial state, and returns the result and the updated state. |
17:57 | <@MyCatVerbs> | So yes. |
17:58 | <@ToxicFrog> | Ok. |
17:58 | <@ToxicFrog> | So the basic idea is that everything in the call chain to functions that use the RNG have, instead of type foo, type State RandomGen foo |
17:58 | <@MyCatVerbs> | Yup. |
17:59 | <@MyCatVerbs> | Did I mention that I originally wrote this without using the State monad, just passing the RNG state around directly? Oh boy was that ever dreadful. |
17:59 | <@ToxicFrog> | The top level is called with runState, giving it its initial state; within these functions, get and put can be used to access the state. |
17:59 | <@ToxicFrog> | Yeah. Been there. |
18:00 | <@MyCatVerbs> | Yup. |
18:00 | <@MyCatVerbs> | I still think it was worth doing, just for the discipline. :) |
18:00 | | * ToxicFrog makes the universal gesture for OH GOD THERE'S FOAM IN MY BRAIN |
18:00 | <@MyCatVerbs> | But that version was full of bugs because there was so much crap to take care of with parameter-passing that I hadn't actually written the algorithm correctly. |
18:01 | | * ToxicFrog stabs Hugs in the face |
18:01 | <@ToxicFrog> | What interactive Haskell terp do you recommend? |
18:01 | <@MyCatVerbs> | ghci |
18:01 | <@MyCatVerbs> | ghci, ghci, ghci and ghci. |
18:02 | <@ToxicFrog> | Ok, how do I load a module like, say, Control.Monad.State in it? |
18:02 | <@MyCatVerbs> | :m Control.Monad.State |
18:02 | <@MyCatVerbs> | Or, more often, :m +Control.Monad.State (to add to the module list, rather than replace the current module list) |
18:02 | <@ToxicFrog> | Aah, that makes more sense |
18:03 | <@ToxicFrog> | The type of runState is actually runState :: State s a -> s -> (a, s) |
18:04 | <@MyCatVerbs> | Jah. (->) binds to the right. |
18:04 | <@ToxicFrog> | ...hmm. This implies - insofar that ordering is meaningful here - that calling a function with type State s a does not actually perform any computation, but returns a value representing the computation to be performed. |
18:04 | <@ToxicFrog> | And runState then causes the actual computation to happen. |
18:04 | <@ToxicFrog> | Cool. |
18:04 | <@MyCatVerbs> | (Int -> Int -> Int) is the same thing as (Int -> (Int -> (Int))) |
18:05 | <@MyCatVerbs> | It's done that way to express the currying (Schonfinkelisation, I think, but don't hold me to that spelling). |
18:05 | <@ToxicFrog> | Yeah, I know |
18:05 | <@MyCatVerbs> | e.g. (+) 3 :: (Num a) => a -> a, et al. |
18:06 | <@ToxicFrog> | Just not firing on all cylinders today, your earlier parenthesized version confused me |
18:06 | <@ToxicFrog> | since I only use when I'm explicitly using high-order functions |
18:06 | <@MyCatVerbs> | I wrote the type of runState with the parens pointed rightward because I was trying to emphasize the fact that you're calling it like, let (value,newstate) = (runState statething) initialstate |
18:07 | <@ToxicFrog> | But you don't need to, you can just as sensically use: |
18:07 | <@ToxicFrog> | let (val,state) = (runState computron initialstate) |
18:08 | <@MyCatVerbs> | Yuss. |
18:08 | <@ToxicFrog> | Which IMO is clearer, since while they're equivalent in implementation, notionally runState takes two arguments and returns a pair, rather than taking one argument and returning a function. |
18:08 | <@MyCatVerbs> | Though you can pass (runState computron) to anything that expects a Whatever -> (Thingy,Whatever). |
18:08 | <@ToxicFrog> | Yes. |
18:09 | <@MyCatVerbs> | Naturally, an experienced Haskell hacker, upon seeing (Whatever -> (Thingy,Whatever)), naturally will ask for a State Thingy Whatever instead. ;) |
18:12 | <@ToxicFrog> | Ok. I think I understand it now; thanks. |
18:13 | <@ToxicFrog> | It's still not as transparent as I would like, though. |
18:15 | <@MyCatVerbs> | If you can think of anything else to ask, smack me. |
18:16 | <@ToxicFrog> | ...although, I wonder if I could just trust the type inference engine to figure out which functions need State for me. |
18:16 | <@ToxicFrog> | Hmm. But they all have to use do-notation, don't they? |
18:16 | <@MyCatVerbs> | No, but it's mighty convenient to. |
18:17 | <@MyCatVerbs> | The type inference system won't detect where you're using State - it's a newtype, not a type alias, so you need to put the constructors in directly. |
18:17 | <@ToxicFrog> | Aah well. |
18:17 | <@ToxicFrog> | (what I was hoping was that the functions at the top and bottom of the call chain could have type State and the compiler would figure out which intermediate ones need it itself) |
18:17 | <@MyCatVerbs> | e.g. (return . (*2)) :: (Monad m, Num a) => a -> m a, but it doesn't know *which* Monad. :) |
18:18 | <@MyCatVerbs> | Yes, it does that sort of local stuff quite happily. |
18:18 | <@MyCatVerbs> | What I meant is that most things can't be made totally invisible without type-checking becoming undecidable. |
18:19 | <@ToxicFrog> | Aah. |
18:19 | <@ToxicFrog> | I want totally invisible state, though ?? |
18:19 | <@ToxicFrog> | What I really want is a hybrid of Lua, Haskell, and Scheme. |
18:20 | <@MyCatVerbs> | Personally I wouldn't bother with Scheme now that I have Haskell on hand. |
18:21 | <@MyCatVerbs> | For one thing, functional languages are *much* closer to isomorphic to one another than imperative languages are, in general. |
18:21 | <@MyCatVerbs> | And I haven't managed to find anything in Scheme that I don't have in Haskell. Hell, I can barely find anything in C++. |
18:21 | <@ToxicFrog> | What I like in Scheme is its macro capabilities. |
18:22 | <@MyCatVerbs> | Okay, backtrack. |
18:22 | <@ToxicFrog> | It wins heavily at CTMP. |
18:22 | <@ToxicFrog> | I will grant that they're less needed in Haskell than in Lua. |
18:22 | <@MyCatVerbs> | I haven't managed to find anything in Scheme that I don't have in Haskell except macros. |
18:22 | <@MyCatVerbs> | In theory, Haskell doesn't need macros for code because combinators are just as powerful (thanks to lazy evaluation). |
18:23 | <@ToxicFrog> | Basically, I want the macro facilities from Scheme, the type system from Haskell [<3], and the painless mutable state and C interface of Lua. |
18:23 | <@MyCatVerbs> | In practice, it comes up. There is a macro system for Haskell, called Template Haskell, but because Haskell doesn't have homoiconic representation like Scheme, it never quite keeps up with the latest language extensions. If you're outputting H98, though, it's fine. |
18:23 | <@ToxicFrog> | And grammar somewhere between the three. |
18:25 | <@MyCatVerbs> | Calling C from Haskell is surprisingly un-painful. The FFI is really nice. (Naturally, pretty much everything ends up in the IO monad, but if you're confident that a particular C function has no side-effects then you're quite free to either import it outside the IO monad or just use unsafePerformIO to call it in pure code). |
18:25 | <@MyCatVerbs> | Calling Haskell from C, I haven't tried. It's not as nice to embed as Lua's terp, though, by reputation. (I'd be disappointed if it was, considering Lua was deliberately designed for exactly this purpose.) |
18:26 | <@MyCatVerbs> | As for easy mutability, well... |
18:26 | <@ToxicFrog> | Yeah, I know, Not The Point. |
18:27 | <@MyCatVerbs> | No. |
18:27 | <@MyCatVerbs> | Y'know how C++ starts from basically nothing, and tries (and fails, IMNSHO) to work up to abstraction? |
18:27 | <@MyCatVerbs> | Haskell is the anti-C++. Haskell starts from abstraction and tries to work towards practicality. |
18:27 | <@ToxicFrog> | Yes. |
18:28 | <@MyCatVerbs> | If you look in the right section of the libraries, you will find raw pointer manipulation primitives, mutable state out the wazoo, direct ways to fuck around with the garbage collector, etc. |
18:28 | <@ToxicFrog> | Yes, but they all require added stuff, like the State monad. |
18:28 | <@ToxicFrog> | They aren't transparent. |
18:29 | <@MyCatVerbs> | Nope. Never will be. |
18:29 | <@MyCatVerbs> | State isn't particularly onerous, though, right? |
18:29 | <@ToxicFrog> | The thing is, I really like FP. But I also really like automatic mutable state. |
18:29 | <@ToxicFrog> | No, it's not. |
18:30 | <@ToxicFrog> | But it's still more noticeable than I would like. |
18:30 | <@ToxicFrog> | The feature from Haskell that I really love is the type system. The rest I'm largely indifferent to. |
18:30 | <@MyCatVerbs> | Usually, use ST in the places where you can't get good asymptotic complexity without mutable state. |
18:30 | <@MyCatVerbs> | ToxicFrog: the differentiation between pure and impure code is a part of that type system. |
18:31 | <@ToxicFrog> | Yes. But you could still have typeclasses and so forth without that. |
18:31 | <@MyCatVerbs> | ToxicFrog: in other languages, effectively everything is in the IO monad. |
18:31 | <@MyCatVerbs> | Yes you can. |
18:31 | <@MyCatVerbs> | An example of where this comes up is "map". |
18:31 | <@ToxicFrog> | I just can't think of any other languages that do, not with the same degree of elegance. |
18:31 | <@MyCatVerbs> | Oh I know of one. :) |
18:32 | <@ToxicFrog> | The problem is that while I love Haskell types to bits, the language as a whole sits poorly with me. |
18:32 | <@MyCatVerbs> | Unfortunately, I don't think there are any implementations. Heh. |
18:32 | <@ToxicFrog> | What's the language? |
18:32 | <@MyCatVerbs> | It was called, I think, System O. |
18:34 | <@MyCatVerbs> | It was outlined in a paper where a couple of the Haskell deities (SPJ and a few others whom I can't remember, unfortunately) first outlined Haskell's typeclasses system, then outlined its failings, then drafted a much better system. |
18:34 | <@ToxicFrog> | Google knows of it not. |
18:34 | <@MyCatVerbs> | (Based on what is, from the type-checker's point of view, only a very minor generalisation of typeclasses.) |
18:35 | <@ToxicFrog> | Part of what I like about Haskell types, I think, is that it has provisions for the sort of duck-typing approach I normally use |
18:35 | <@MyCatVerbs> | Yeah, I don't think they ever had a non-toy implementation. Hell, I'm not sure whether they ever had an implementation at all. Not actually necessary to implement it to prove it possible, y'know. |
18:35 | <@MyCatVerbs> | s/y'know./y'know?/ |
18:36 | <@MyCatVerbs> | Anyway. What purity *does* buy you is that it makes laziness possible (because now evaluation order doesn't matter) and it also makes a lot of transformations a lot easier. |
18:36 | <@ToxicFrog> | Yeah. |
18:37 | <@ToxicFrog> | I know this. |
18:37 | <@MyCatVerbs> | For example, map f . map g == map (f . g), in Haskell. In SML, nope, not unless the compiler has proved that neither f nor g touch any mutable refs. |
18:37 | <@ToxicFrog> | It's just much less a priority for me. |
18:37 | <@MyCatVerbs> | *shrug* I quite like having a powerful optimizing compiler. |
18:38 | <@MyCatVerbs> | And GHC is getting, uh, surprisingly powerful, especially now that rewrite rules are starting to pervade the libraries. |
18:38 | <@MyCatVerbs> | But also, things like how, in ML, map takes O(N) space, and is a single monolithic O(N) operation. |
18:39 | | Serah [~Z@87.72.35.ns-26506] has quit [Ping Timeout] |
18:39 | <@ToxicFrog> | I've never used ML, so you can't assume anything about my knowledge of it. |
18:39 | <@ToxicFrog> | Purely monomorphic operators always scare me off. |
18:40 | <@MyCatVerbs> | Y'know how almost _all_ functional languages are isomorphic to a much greater degree than almost _any_ imperative languages? |
18:40 | <@MyCatVerbs> | ML is basically Scheme with a strong type system and infix syntax. |
18:40 | <@MyCatVerbs> | In Haskell, map takes O(1) space and you can get the first element right away. |
18:40 | <@ToxicFrog> | Given the vast differences between ML and Haskell, I dispute this. |
18:41 | <@MyCatVerbs> | Nah. You can embed Haskell in Scheme by using functions to emulate lazy thunks. |
18:42 | <@MyCatVerbs> | And vice-versa, you can embed Scheme in Haskell by forcing thunks right away. |
18:45 | <@MyCatVerbs> | But anyway, ML's semantics are pretty close to Scheme's. At least, all the stuff that *matters* is, anyway. (Oh, you don't get call/cc, but you're a smart kid, right? You can write continuation-passing code manually quite happily, right? :) |
19:00 | < Doctor_Nick> | computers |
19:09 | <@MyCatVerbs> | They're fuckin' awesome, don't you agree? |
19:09 | < Doctor_Nick> | NO |
19:09 | < Doctor_Nick> | I HATE THEM |
19:09 | < Doctor_Nick> | AGAGAATARARAG |
19:09 | < Doctor_Nick> | SMASH |
19:09 | < Doctor_Nick> | gesagsakjgge;oijgd;ojbf;oijfdj |
19:09 | < Doctor_Nick> | ha |
19:09 | < Doctor_Nick> | showed that keyboard |
19:13 | | * TheWatcher readsup, notes that he has yet to actually be given a convincing reason why functional programming is actually worth it in the real world |
19:15 | <@TheWatcher> | (but then, I actually like c, c++ and perl, so my view of things probably doesn't count as I'm clearly insane) |
19:26 | <@ToxicFrog> | TW: because it's easy to verify and easy to optimize. If you expand FP to include impure languages in which you can do FP, it also tends to be much more expressive. |
20:24 | <@McMartin> | Because a fucked up switch statement brought down the entire Eastern Seaboard's communication nets, and Erlang can't have that error~ |
20:25 | <@MyCatVerbs> | McMartin: it can have analogous errors, though. Like accidentally putting a wildcard in a case expression, say. |
20:26 | <@MyCatVerbs> | McMartin: trashing the heap, destroying data structures while they're still in use by other parts of the program, etc, don't happen, though. |
20:28 | <@McMartin> | Well, the latter can still happen in the practical impure languages. |
20:29 | <@McMartin> | You can do it Lisp and OCaml |
20:29 | <@McMartin> | And, well, Python, which has most of that. |
20:30 | <@MyCatVerbs> | Yeah, but it's fantastically unlikely, since all three almost always use immutable data structures. |
20:31 | <@MyCatVerbs> | (Normal Python style is to cons up new data structures rather than destructively alter them in-place, right?) |
20:31 | <@MyCatVerbs> | s/them/old ones/ |
20:31 | <@McMartin> | (Depends a lot on which data type you're using.) |
20:31 | <@MyCatVerbs> | It's not so much "can't" as "doesn't". |
20:31 | <@Vornicus> | (What McM said.) |
20:31 | <@McMartin> | (OCaml and Python both make heavy use of mutation in array types.) |
20:31 | <@McMartin> | (And, uh, hashtables) |
20:32 | <@MyCatVerbs> | Hell, I can trash the heap in Haskell in pure code quite easily. |
20:33 | <@MyCatVerbs> | unsafePerformIO (randomRIO (0,maxBound::WordPtr) >>= poke 400 . wordPtrToPtr >> return "maybe your computer just blew up, maybe not") |
20:33 | <@MyCatVerbs> | Nobody does, though. |
20:34 | <@ToxicFrog> | unsafePerformIO is pure? |
20:35 | <@MyCatVerbs> | ToxicFrog: no, but it can be called from pure code. I could hide the above time-bomb in a module with pure-looking exports. |
20:37 | <@MyCatVerbs> | unsafePerformIO creates a thunk which, when evaluated, performs the IO action, and returns whatever that IO action returned. Or, in a GHC-ism, creates a new universe, passes it into the IO action (BTW: "IO a" is, internally to GHC, and semantically, a synonym for (RealWorld -> (a,RealWorld)), funkily enough) and then throws the universe away. |
20:38 | <@McMartin> | Ah, monads |
20:38 | <@MyCatVerbs> | McMartin: strangely enough, this particular oddity isn't really related to monads at all. |
20:39 | <@MyCatVerbs> | McMartin: it's possible to implement IO in pure FP languages via explicit universe-passing, it's just that using the IO monad makes it much less tedious. |
20:40 | <@MyCatVerbs> | McMartin: and, of course, if you use _explicit_ universe-passing then you need uniqueness typing to make sure that you don't accidentally alias the universe. |
20:40 | <@McMartin> | Right |
20:40 | <@MyCatVerbs> | (It would probably be very bad indeed to attempt to work on an outdated version of the universe, right? :) |
20:41 | <@McMartin> | But I was given to understand that monads were syntactic sugar for the uniqueness typing. |
20:41 | <@McMartin> | "Monad" is a term from abstract algebra that applies to the explicit universe-passing as well. |
20:41 | <@MyCatVerbs> | No. The IO monad hides the RealWorld from you in a way that's just as good as having uniqueness for it. Not the same thing. |
20:42 | <@MyCatVerbs> | Also no. Monads are originally a weird thing from category theory, with a formulation that I don't actually understand (because I don't know cat theory - it's bloody dense and I got headaches last time I tried to pick it up). |
20:42 | <@MyCatVerbs> | If you encounter them in a categorical setting, they're likely to be called "triples", IIRC. |
20:43 | <@MyCatVerbs> | There's nothing inherent in the concept of a monad about universe-passing. Rather, it's the opposite way around. |
20:44 | <@MyCatVerbs> | Instead, you can construct a monad that does correct universe-passing for you and call it the "IO monad". :) |
20:45 | <@MyCatVerbs> | McMartin: uniqueness typing is a really powerful thing in and of itself. If I were going to start randomly adding features to Haskell, either it or linear typing would be near the top of the list, because there are applications where it's useful to be able to prove that you never accidentally duplicate a particular value - in compilers, for example. :) |
20:45 | <@McMartin> | Monads absolutely exist in AA. They're a set, with a binary operation under which they are closed, and for which an identity exists. |
20:45 | <@McMartin> | These properties hold for universes under IO. |
20:46 | <@McMartin> | Also, re: yesterdsay's discussion; the bit he wanted to rip out of the Design Patterns book was the "Interpreter" pattern. |
20:47 | <@MyCatVerbs> | McMartin: k, but they originate in cat theory, and that's where Moggi got the idea from. |
20:47 | <@ToxicFrog> | Which...appears to be exactly what it says on the box. |
20:48 | <@MyCatVerbs> | (Er, the idea of applying them for structuring computations.) |
20:49 | <@MyCatVerbs> | McMartin: The Interpreter "Design Pattern" sounds an awful lot like someone cribbed the "little languges" idea. |
20:51 | <@McMartin> | Design patterns are entirely about cribbing important things missing from C++ and Smalltalk and implementing them as higher-level macros. =P |
20:52 | <@MyCatVerbs> | Smalltalk isn't missing all that much, is it? |
20:53 | <@McMartin> | Among OO, multiple dispatch and morphing |
20:53 | <@McMartin> | Among everything else, it misses most of the same things that OO languages traditionally miss |
20:58 | <@MyCatVerbs> | Morphing? You mean altering individual objects (adding/removing methods or fields) at runtime? |
20:58 | <@MyCatVerbs> | Or something else? |
20:59 | <@MyCatVerbs> | McMartin: Smalltalk has closures, doesn't it? In the form of blocks, I mean. |
21:05 | <@McMartin> | I forget how powerful blocks are |
21:05 | <@McMartin> | But I don't think you can play the kinds of games with lexical scoping that you can in say LISP. |
21:06 | <@McMartin> | Never actually used Smalltalk though |
22:16 | | You're now known as TheWatcher[T-2] |
22:23 | | You're now known as TheWatcher[zZzZ] |
--- Log closed Sat Jul 26 00:00:07 2008 |