IRC channel logs


back to list of logs

<nalaginrut>morning guilers~
<mark_weaver>good evening!
<DeeEff>Hey guilers, of the three types of hash functions (hash, hashq, and hashv) which is the best to use for general purpose? So far I've been using just hash-set! and hash-get-handle since they use equal? (which by extension allows me to set keys to be strings) but is there one that is more generally used than the others?
<mark_weaver>there's no such thing as "general purpose" here. you need to pick the one that's appropriate for your use case. if you want strings for keys, then you need 'hash'.
<DeeEff>but in such a case, setting symbols as keys works as well, as does with numbers. Is that not the more generic type then?
<mark_weaver>I'm sorry, I need to know more about your use case to make suggestions.
<mark_weaver>there's no "one size fits all" advice here.
<DeeEff>it's not particularly a use case issue. My question concerns how you would use a hash table in general. Typically, you'd expect that a hash-table can have any arbitrary value or type for keys, and any arbitrary value or type for values.
<DeeEff>Based on that, hash functions tend to appear to be more flexible, as they use equal? to evaluate between key matching
<DeeEff>e.g. (hash-get-handle h "some string")
<DeeEff>will return ("some string" . value) if it exists, and #f if it doesn't
<mark_weaver>I can tell you that for any _mutable_ data structure, eqv? compares by identity, not by value. so (eqv? (list 1 2) (list 1 2)) returns false, because although those two lists contain the same values, they are not the same objects: mutating one will not mutate the other.
<mark_weaver>whereas 'equal?' compares by value
<mark_weaver>so I suppose that most of the time, 'hash' and 'equal?' is the safest pick unless you want to compare by object identity rather than value.
<DeeEff>Which is why I asked.
<DeeEff>arguably, you wouldn't need eqv? in almost all cases though
<ijp>I use eqv? hashtables all the time
<DeeEff>since keys need to be removed and set using state change (hash-remove! and hash-set! respectively)
<ijp>eqv? and strings probably make up 99% of my uses
<ijp>anyway, I'd recommend the srfi-69 hashtables api
<DeeEff>what is the advantage of srfi-69 over guile built-ins?
<ijp>not having to keep saying what the hashing function is over and over
<DeeEff>so you can basically set the equal-proc at define time, as well as the hash-proc.
<DeeEff>what form does the hash-proc take? It says it can be any two argument procedure, but what is a typical implementation?
<DeeEff>thanks for the help, by the way
<mark_weaver>well, srfi-69 provides a few hash procedures that are often sufficient: hash, string-hash, string-ci-hash, and hash-by-identity
<DeeEff>I'm not sure I understand why a hash-proc is needed, is a hash-table not just an associative array?
<mark_weaver>do you know how hash tables work?
<DeeEff>I'm currently experimenting with them, but I have an idea.
<ijp>creating a good hash function is more art than science, I think
<mark_weaver>in order for a hash table to perform well, you need a good hash function (hash-proc).
<mark_weaver>(hash a) and (hash b) must return the same number if 'a' and 'b' are to be considered equal.
<DeeEff>does it have to be a number? For example, an MD5 signature instead?
<mark_weaver>and importantly (for efficiency): the numbers returned by 'hash' should be as evenly distributed as possible.
<ijp>an md5 signature is a number
<ijp>it's even in hex
<DeeEff>nevermind then, here I figured it was a string
<mark_weaver>DeeEff: I suggest you read the wikipedia article on hash tables.
<mark_weaver>also, a good hash function should be cheap to compute.
<DeeEff>alright, thanks, I think I've got enough to understand the basic gist of it now.
<mark_weaver>you're welcome!
<mark_weaver>greetings, minikomi!
<minikomi>why thank you mark
<nisstyre>Does Guile have a built-in curry procedure? i.e. (curry f a b) which gives you a procedure f that is curried and also applies a and b to it?
<nisstyre>I did google but could not find much
<mark_weaver>'cut' might be something like what you want, but your description is not very clear.
<nisstyre>mark_weaver: that would be like (((curry f) a) b)
<nisstyre>where f takes > 2 arguments
<nisstyre>and curry transforms it into a function that is curried
<nisstyre>or maybe just fixes those parameters I guess
<nisstyre>it doesn't matter for my purposes
<mark_weaver>if 'f' takes 4 arguments, then (cut f a b <> <>) returns a function that takes the last two arguments, with 'a' and 'b' fixed.
<nisstyre>it could just be (define (curry fun . args) (lambda x (apply fun (append args x))))
<nisstyre>I'm wondering if that is builtin
<mark_weaver>part of the problem with a general mechanism along the lines you suggest is that in scheme, procedures don't have fixed arity, in general.
<nisstyre>mark_weaver: okay I'll look at cut then
<nisstyre>mark_weaver: yeah, so the actual syntactic transformation sometimes makes no sense
<nisstyre>I get it
<mark_weaver>but it should be easy to define what you want, if you can explain it clearly enough for me to understand. some concrete examples would be helpful.
<nisstyre>like (curry list) would make no sense if it was actual currying
<ijp>I object to the use of the name curry for that procedure
<nisstyre>mark_weaver: actually I just wanted to transform this code to Guile, it's in Racket:
<nisstyre>before you object to my use of call/cc, it is just for explanatory purposes
<nisstyre>ijp: yeah it is a bad name I guess
<mark_weaver>I just read the racket docs for 'curry', and IMO it's a bit strange.
<nisstyre>well, yeah it is :P
<nalaginrut>nisstyre: (cut select-first-help elem? xs <>) for you case, IMO
<nisstyre>nalaginrut: that works, thanks
<mark_weaver>the first time the resulting procedure is called, it returns another procedure unless the *maximum* number of allowable arguments is passed. after that, it returns a procedure only if not enough arguments have been passed.
<nisstyre>mark_weaver: yeah, so for procedures with an unbounded arity it will always return a procedure
<mark_weaver>so (curry list) returns a procedure, ((curry list) ...) returns a procedure, and (((curry list) ...) ...) returns a list. it's a strange assymmetry in how the first call and the later calls are treated.
<mark_weaver>I could implement it in guile, but I'm not sure I want to :)
<nisstyre>fair point
<mark_weaver>'cut' and 'cute' are standardized (SRFI-26) and fairly widely used.
<mark_weaver>also see <...> in the description of 'cut', which maybe does something like what you wanted, more generally.
<nisstyre>mark_weaver: thanks for the info, did not know about cut
<stis>snowly greetings guilers!
<wingo>is it snowing where you are?
<wingo>it is 20 degrees and lovely here
<stis>hehe, here is around 0 and snowing.
<stis>well i guess it's in the genes, I find it cozy somehow.
<stis>hiding inside and listen to the childeren cursing the weather ;-)
<stis>I've actually spent last week in the swedisch montains, skiing with lot's of friends.
<stis>lovely. And jokes aside, the children usually loves playing outside in the snow.
<stis>+2 degrres and muddy is mucjh much worse.
<tupi>30 deg blue sky here :)
<stis>ok ok, that's even better :-)
<stis>wingo: great hack re: stack
<davexunit>I enjoyed that rhyme
<stis>I can't count the number of times I redesigned algorithms due to stack overflow at deep recursion
<lloda>re:stack, master make check fails me... is this expected?
<lloda>GC Warning: Failed to expand heap by 2797568 bytes
<lloda>(several times)
<lloda>then GC Warning: Out of Memory! Heap size: 8 MiB. Returning NULL!
<lloda>and segfault. some bad options with bdwgc?
<mark_weaver>wingo: did you see
<mark_weaver>maybe it's time for 2.0.11 :-/
<ijp>you wait ages for a release, and then two come along at once
<mark_weaver>we seem to have bad luck with even numbered releases.
***ozzloy_ is now known as ozzloy
<lloda>I should have caught the bug earlier, but 2.0.10 doesn't have patches I need (they're in master) and I can't use master either because of compiler issues, so I'm still on 2.0.9 + patches :-/
<lloda>we need to cover this in the test suite.
<mark_weaver>what problem do you have with master?
<mark_weaver>(the "indeed" was in response to "we need to cover this in the test suite.")
<lloda>the compiler doesn't take literal arrays, so a lot of my files go through the interpreter it seems. Then I get lots of errors. I haven't pinpointed the exact reason.
<mark_weaver>ah, okay.
<zacts>mark_weaver: testing my latest patch on redports, but I'm getting 'success' for most of my builds. I'm expecting full success for my latest build.
<mark_weaver>that's good news!
<zacts>and once that happens we shall be close to a PR.
<mark_weaver>unfortunately, it seems that 2.0.10 has a serious bug and we'll have to release 2.0.11 soon.
<zacts>mark_weaver: really..
<zacts>what bug is that?
<mark_weaver>anyway, if 2.0.10 works on freebsd, surely 2.0.11 will as well.
<zacts>ah ok. cool, well I can easily update my Makefile and test again on redports.
<zacts>I guess redports is the FreeBSD equivalent of hydra.
<mark_weaver>cool :)
<ArneBab_>The manual shows the meta switch for scripts using a script started with /usr/local/bin/guile. Can it be made to work with #!/usr/bin/env guile?
<ArneBab_>(so it does not depend on the location of the guile binary)
<mark_weaver>ArneBab_: unfortunately, no. a shebang can take only one argument. if the program is /usr/bin/env, then guile has to be the sole argument, and there's no room left for the \\
<ArneBab_>then I still need to get this working:
<ArneBab_>run code only if a file was called as script, but not if imported as module
*ArneBab_ is currently looking into that again)
<Madsy>Stupid question as I'm still new to Scheme. When a nontrivial program grows in size, you want to split it up into several files. How do I handle that?
<Madsy>Modules are fine, but sometimes you want to split stuff even though it's not a new module
<Madsy>Do I just use load ?
<mark_weaver>you can use load, sure. if the argument to load is a literal relative pathname, it is interpreted relative to the location of the file that did the load.
<Madsy>And the entrypoint is just whatever function which is called at toplevel, right?
<mark_weaver>I'm sorry, I don't understand your question. I think I'm lacking context. does this relate to 'load'?
<Madsy>No, a separate question. Other languages have an entrypoint function. The first function to get called. But I guess that make less sense in Scheme
<mark_weaver>the entry point depends on how guile was invoked.
<mark_weaver>when guile loads a file or script, all of the top-level expressions are evaluated, so you can do stuff there.
<mark_weaver>there's also a '-e' option to specify a procedure to call after loading.
<mark_weaver>and of course, if you are using libguile from a C program, then it's up to your C program to call something.
<mark_weaver>ArneBab_: if you need to make a module that's also runnable as a script (without using a separate program), and where you also can't hard-code the location of the guile executable, then I guess you'll have to make the top of the script a shell script, similar to what's done in meta/guild
<ArneBab_>mark_weaver: I had expected that to look much more complex, but actually it seems straightforward…
<mark_weaver>it could be simpler than 'guild' for your use case.
<mark_weaver>the third line could simply be: exec guile -e '(@@ (MODULE NAME) main)' -s "@0" "$@"
<mark_weaver>and then the !#
<mark_weaver>I still think it's better to make a separate script that loads the module and runs the included tests.
<ArneBab_>so the MODULE NAME would still have to contain the name of the module I’m currently working on?
<mark_weaver>(and I wrote that script for you)
<ArneBab_>a separate script is also possible, yes, but I’m specifically comparing use-cases here.
<ArneBab_>and this asks “can guile realize this very common usecase from python?”
<ArneBab_>(python can also just run the tests from outside, but still the “check if this was run directly”-method is very widespread)
<mark_weaver>well, you're asking whether guile can do things the python way, which is a bit unfair.
<ArneBab_>mark_weaver: yes, it is unfair: This is what a Python-programmer will ask. That’s why there’s a section after that which looks at the things guile offers which python does not.
<mark_weaver>is the python way to make module files executable?
<mark_weaver>or do you generally do "python" ?
<ArneBab_>it rather is the python-way to start with scripts and turn them into modules afterwards (while keeping the script functionality)
<ArneBab_>both are widespread
<ArneBab_>there’s even python -m module which runs the module as script
<mark_weaver>FWIW, I do think that we could improve Guile's handling of script launching.
<ArneBab_>which leads to doing profiling with python -m profile
<mark_weaver>ideally, it should be possible to put "#!/usr/bin/env guile" at the top, and _still_ have something like the meta switch at the top of the script.
<mark_weaver>my other peeve is that it should be possible to put the meta switch arguments on the third line instead of the second line, so that you can put -*- Scheme -*- on the second line.
<mark_weaver>(Emacs looks only on the first two lines for that, iirc)
<ArneBab_>that would be pretty nice, yes
<mark_weaver>so, I agree that there are improvements to be made here. at the same time, it seems to me that the separate script method is pretty good here too.
<mark_weaver>one should expect that when switching languages, some things will have to be done slightly differently.
<ArneBab_>that definitely - and for larger projects it wins, because it keeps the testing logic together in one file and consistent.
<ArneBab_>mark_weaver: ideally I’d like to add the different conventions how Guile does things, but I still need to learn them ☺
<mark_weaver>right, I don't think having the __name__ == "__main__" thing in every module is very nice.
<mark_weaver>it seems kind of hacky to me.
<ArneBab_>it isn’t… along with some other parts it shows me that the python-syntax is already stretched to its limit. That’s one of the reasons why I am looking into guile
<ArneBab_>“this is how to replicate the python way of doing this - and that’s how schemers do it, because X”
<mark_weaver>sure, makes sense. fwiw, I appreciate your work on this article.
<ArneBab_>You already said so, but thanks ☺
<ArneBab_>(I’m glad the article is useful)
<ArneBab_>Actually I’m writing it, because Scheme is really fascinating and Guile Scheme looks like it could fulfill the role which python currently has for me - while giving me the freedom I’m missing in Python-Programming.
<mark_weaver>heh, I prefer to err on the side of too many thanks than too few. maybe it's annoying sometimes :-/
<ArneBab_>it’s definitely not annoying but rather cheers me up ☺
<mark_weaver>that's good :)
<ArneBab_>and it’s especially nice during discussions: We might disagree, but I still like what you’re doing
<mark_weaver>right, that's why I repeated it in this case.
<mark_weaver>on the more critical side: the last I checked, it seemed that you kept the section on how loops aren't nice to write in scheme, and only later talk about the nice looping macros that are available. it seems like that earlier section no longer really belongs, but only demonstrated your lack of knowledge at the time, no?
<ArneBab_>I still need to adjust the text ☺
<mark_weaver>fwiw, almost no one ever uses Scheme 'do'.
<ArneBab_>I might be able to just split it into “initial impression” and “yes, it can”
<mark_weaver>recursion is really the way to do loops in Scheme's core.
<ArneBab_>and let-recursion is really elegant
<ArneBab_>(though my mind has started adjusting after reading the little schemer…)
<ArneBab_>(so I might no longer feel the real impression Pythonistas get)
<mark_weaver>fwiw, recursion can be made a little easier to read for non-schemers by avoiding even the use of named-let, and instead just defining an inner procedure that calls itself to loop, and then calling that procedure from the outside to start the loop. SICP always does it that way; they never use named-let.
<mark_weaver>and actually, if you name the inner procedure properly, it makes for very readable code.
<ijp>so no "loop" or "lp" or "recur"
<davexunit>I find myself using inner procedures instead of named lets. I like the way it reads.
<mark_weaver>yeah, instead, name the procedure such that it makes sense as a procedure that does the partial job. (whatever is left to be done at a given point in the loop)
<ArneBab_>you mean something like (define (addfirst N) (define (addnext cur step last) (if (equal? step last) (+ cur step) (addnext (+ cur step) (1+ step) N)))(addnext 0 0 N))
<mark_weaver>(let loop ((a init-a) (b init-b) ...) body ...) is equivalent to: (let () (define (loop a b) body ...) (loop init-a init-b))
<wingo_>mark_weaver: bummer :/
<wingo_>sorry about that :(
<wingo_>(the api breakage)
<mark_weaver>yeah, too bad we didn't have enough tests for this API.
<mark_weaver>oh well
<ArneBab_>I actually prefer the let-recursion: It avoids the unnecessary call at the end
<ArneBab_>though I do not understand why people call it (let loop …) instead of using a more descriptive name
<civodul>mark_weaver: agreed, we must definitely have tests for that
<mark_weaver>ArneBab_: well, I would use different names there. if the names aren't good, it doesn't make for readable code.
<mark_weaver>basically the idea is to make a more general procedure.
***wingo_ is now known as wingo
<mark_weaver>which can be used to do the final part of the job.
<ArneBab_>here’s a short comparison of both:
<mark_weaver>IMO, the names you've chosen are not very good. It's not obvious what these procedures do.
<ArneBab_>what would you use?
<mark_weaver>do they add up the integers from 1 to N ?
<ijp>triangular numbers a.k.a. don't start with with that story about gauss we've all heard it a million times
<mark_weaver>if so (and I deliberately wanted to avoid the closed-form formula for this) I would write it (define (add-first n) (if (zero? n) (+ n (add-first (- n 1)))))
<mark_weaver>but if I wanted to add them in ascending order, I would envision a more general procedure that adds the integers between A and B, then then write:
<mark_weaver>well, plus an accumulator
<mark_weaver>so something like: (define (integer-range-sum-plus accum a b) (if (> a b) accum (integer-range-sum-plus a (+ a 1) b)))
<mark_weaver>and then (define (add-first n) (integer-range-sum-plus 0 1 n))
<mark_weaver>maybe not great names, but that's the basic idea.
<ijp>(stream-ref (running-sum integers) n)
<davexunit>ijp: nice
<mark_weaver>well, I'm deliberately avoid fancy things here.
<didi``>davexunit: I do that too. My dream is to stop writing loops altogether.
<civodul>while we're at it:
<civodul>scheme@(guile-user)> (close(current-output-port ))
<civodul>Segmentation fault
<ijp>mark_weaver: no, the fancy bit is where I apply stream fusion and end up at the beginning
<ArneBab_>mark_weaver: this uses an external function, but I guess you could just define it internally.
<ijp>civodul: omg!
<mark_weaver>ArneBab_: right, better to make it internal
<ArneBab_>what I think really elegant about using named let is that it avoids the additional call to the internal function: The function is intended to be run in any case, and the named-let makes that clear.
<ArneBab_>also it reuses the information provided by let to define the functionality with the least amount of boiler-plate text
<mark_weaver>a matter of taste, I suppose.
<ArneBab_>likely yes ☺
<mark_weaver>ijp: well, if you just want to solve this specific case, then (define (add-first n) (/ (* n (+ n 1)) 2))
<mark_weaver>obviously, this is just an example to show different styles of expressing loops :-P
<ArneBab_>yepp - I chose the simplest example I could think of ☺
<ArneBab_>I need to get sleep now - this is where I got to:
<mark_weaver>okay, good night!
<Madsy>I'm using li = scm_assoc_set_x(li, keyword, var); to create a new entry in an association list, but how do I create a default empty list to initialize "li" ?
<Madsy>Thanks :)
<wingo>mark_weaver: i'm on the test suite if no one else is
<wingo>that bug i mean
<mark_weaver>wingo: sounds good, ethanks!
<wingo>by "test suite" i meant i'm currently making a test case ;)
<wingo>i had no idea that the back and forward navigation keys on my keyboard moved through emacs buffers
<mark_weaver>civodul: the bug there is that 'scm_simple_format' assumes that the current output port is a valid, open port.
<wingo>the web page buttons
<mark_weaver>wingo: oh, interesting :)
<civodul>mark_weaver: ah ok, that's a reasonable bug :-)
<mark_weaver>easily fixed too.
<mark_weaver>I'll do so.
<civodul>thank you!
<mark_weaver>civodul: when do you think we should release 2.0.11?
<civodul>when you tell me it's alright? :-)
<civodul>could be tomorrow
<mark_weaver>tomorrow sounds good to me
<wingo>just pushed the fix for the srfi-4 issue, sorry about that
<wingo>i'll reply on the ml
<mark_weaver>wingo: thanks!
<civodul>thanks, wingo
<wingo>we don't have a way to get a character from its unicode description, do we?
<mark_weaver>hmm, dunno!
<civodul>wingo: we don't, but that'd be nice
<civodul>libunistring has that
*mark_weaver goes afk for a while
<wingo>oh neat, figl is gnu since a few months -- amazing what digging in your inbox will uncover
<wingo>i guess it's time to move everything to savannah and change the name to guile-opengl
<civodul>good night/day!