libera/#commonlisp - IRC Chatlog
Search
12:08:18
Josh_2
Yes but if you check quicklisp-projects issues nothing has been closed for a long time, plus no updates on planet.lisp
12:50:07
pjb
Josh_2: a few years ago he had some health issues. Perhaps he's again busy with that, or a new job?
19:01:33
mathrick
Shinmera: does the MPG123 segment accept files by content, or does it require a pathname and will always try to open it itself? It's not clear to me from reading the docs and source
19:38:19
Shinmera
Fwiw mp3s are a bad idea for games. They're slower to decode, don't loop smoothly, and aren't great for short effects aynhow.
19:53:33
mathrick
Shinmera: because having to specify paths on the local filesystem is really constraining for the rest of the design
19:54:58
mathrick
yes! It's really convenient to have the option of having the library open a file for you, but it's really inconvenient if that's the only way you have to talk to it
19:58:01
Shinmera
And anyway, please don't feel like this is closed source software. Everything is there and can be adapted should the need arise.
19:58:52
mathrick
but I also wanted to know ahead of time, because I don't think I've ever used a library for longer than the tutorial without wanting to use in-memory sources, so it's one of those API corners I explore immediately
20:00:42
Shinmera
any source in cl-mixed can pretty trivially be turned into an in-memory source, anyhow.
20:01:24
mathrick
howso? I thought they'd need to serve uncompressed samples for the rest of the segments to be able to consume them?
20:02:49
mathrick
right, but that would require using the underlying decompression library, which in the case of mpg123 at least isn't supported I thought?
20:03:32
Shinmera
I mean, you read it from file into one uncompressed pack buffer. and then you just use that pack as a source.
20:04:59
Shinmera
a source segment just outputs stuff into a PACK when MIX is called. So you attach a PACK that's big enough to contain all the data, attach it to the source, call MIX, and you got all the data uncompressed and decoded in-memory.
20:06:51
Shinmera
by default harmony takes care of buffer and pack allocation for you, sizing things in accordance to its internal samplerate and buffer length.
20:07:07
Shinmera
but there's nothing stopping you from doing a thingy before hand or manually managing stuff outside harmony.
20:08:00
Shinmera
A feature I haven't tested yet is even providing your own malloc/free for libmixed to use so you could have it allocate everything in a static-vector or something to keep things lisp-side.
20:14:58
gin
is there a more elegant way to do this? (if (zerop (length *a*)) "" (concatenate 'string "prefix: " *a*))
20:15:38
gin
I am trying to return "prefix: " followed by the string only if it is non-empty string. If it is an empty string, just return the empty string.
20:19:26
_death
almost.. (defun nonempty-string-p (x) (if (equal x "") nil x)) then pass (nonempty-string-p *a*) to format
20:20:17
Shinmera
I often have a handy OR* that treats empty strings as NIL, so with that it would be (format .. (or* *a*)) :)
20:22:48
thuna`
I thought there was a format directive that checked for equality between its arguments but am I misremembering?
20:26:31
aeth
there's some point in complexity that's sooner than most people think but later than I usually attempt where with-output-to-string is a way simpler way to make strings
20:27:03
aeth
(I usually spend about 10 minutes on with-output-to-string, give up, and then do it in a very short FORMAT, but I guess that's better than building a FORMAT that's too elaborate)
20:29:05
_death
I had in mind something that pops a small buffer with short directive description as you move around in a format control string
20:32:48
thuna`
I would be surprised if something like cl-ppcre for format strings doesn't exist already
20:36:19
_death
welp. there's (macroexpand-1 '(formatter "insert ~A here")) ;) .. but then there's http://cs-www.cs.yale.edu/homes/dvm/format-stinks.html and derivatives
21:41:19
gin
(let* ((a 10) (a 20)) (print a)) ; <= this prints 20. is there anyway to access the outer a = 10 within LET*?
22:22:15
mathrick
_death: FORMAT is extremely useful. That page is essentially cout for CL, and there's a reason cout is widely regarded to be toxic. No matter how cryptic, format strings are always superior to alternatives, and it's precisely the fact that they separate the printing code from the specification of what to print and how
22:22:51
mathrick
OUT, cout, and anything else that builds strings immediately in the code cannot be localised
22:30:36
_death
heard that one before.. but look, lisp code can easily be treated as data, and vice versa
22:31:29
_death
also, I recommend not think of an OUT operator as something as simplistic as std::cout
22:35:44
aeth
if you're generating strings in a complicated way, then odds are you're generating HTML (or similar), and you don't have to localize "<html>" into other languages, just the body text, which can be done at another step
22:36:03
mathrick
_death: but it's exactly that. It's a macro that expands to code that builds strings out of little chunks, which is exactly what cout does as well, << and "type safety" notwithstanding
22:36:17
jcowan
someone might want to adapt Scheme's SRFI 166 to CL; it provides formatters based on a monadic combinator, but you don't have to know it works to use it:
22:36:45
mathrick
aeth: sure, it's possible to *abuse* FORMAT. But OUT is not a solution to a problem that needs to be solved, and certainly not in the way OUT proposes
22:38:51
_death
mathrick: we can start with the fact that std::cout is not a (lisp analogue of a) macro..
22:41:00
aeth
you can use whatever you want to generate the FORMAT string, though, if you really need to do "a whole lot of formatting | Hello, ~A | a whole lot of formatting"
22:41:02
mathrick
FORMAT, printf, str.format, or whatever $language uses is the right way to do human-oriented string output
22:41:18
mathrick
OUT, cout and other contraptions that build them out of little chunks are just not
22:43:22
_death
a format control string also specifies little chunks, so I'm not sure about that point.. anyway, my experience with an OUT macro suggests that it's not difficult to do whatever it is you want to do with it, although I don't think I needed to localize such forms
22:44:23
mathrick
no, it specifies the entire thing at once. Which means it can be localised, preserving all the context that's possible to preserve, and allowing the pieces to be reordered, which is critical for proper localisation
22:45:33
aeth
In the very real example of generating "<!DOCTYPE html>~%<html lang="en">~%<head><title>Hello!</title></head>~%<body><p>Hello, ~A!</p></body>~%</html>" you only need to use a FORMAT string at this point and at no other point (except, of course, the whole body text is ~A, not just the name in the hello world), and can use whatever you want to generate the string (at compile time, via some HTML macro)
22:45:51
aeth
You absolutely don't want to make a mess of it by doing it all in one FORMAT even though you can
22:45:51
_death
this is wrong, because localization sometimes requires more information than is supplied with ordinary use of format.. it can be information about the arguments, or about the context
22:46:01
mathrick
yes, HTML "strings" are a very different use case, and you should be using a dedicated builder for HTML
22:46:23
aeth
except in practice, almost all of the time you're generating text, you're generating markup, too
22:46:41
aeth
because you need to speak to the outside world, and you'd only do that directly in a GUI or in-terminal app.
22:47:29
_death
so format control strings don't "solve" localization, and they are not the only way to get there
22:48:03
mathrick
_death: but the typical way to do it already solves the problem! It'll be something like (format nil ($$ "Operation ~A failed due to ~A" :context "File save") ... ...), where $$ is the "look up localised string" function
22:49:08
mathrick
and for numeric values, you'll typically have a second variant of it that is informed that a certain value is a count / ordinal, and can localise it according to the language's rules
22:50:29
mathrick
I've spent a lot of time in localisation, and almost everyone gets it wrong, especially if they have "oh, you don't need anything special, and it's a minor thing anyway" attitude
22:50:52
aeth
mathrick: that only happens if you don't separate text (user visible text) from strings, though
22:51:14
mathrick
_death: yeah, I'm looking forward to you extracting this to a single text file the translator can edit to get all the strings translated
22:52:12
mathrick
aeth: but OUT is built *for* building user-visible text! That's what the author advocates it for with the very first example!
22:52:33
aeth
You could do something not unlike gettext. A reader macro where you write the English (or whatever the original language is) and then that's actually looking up the translation. _"Hello, world!"
22:53:09
_death
mathrick: why is your translator editing a text file? isn't it better to have a program with a GUI that can be a bit more ambitious with regards to localization work
22:53:30
mathrick
aeth: yes. And gettext() relies on there being a single string. They even have a document explaining why printf() is better than cout :)
22:54:20
mathrick
if you have (outs (:$ "Operation " op " failed due to " reason (:context "File save"))), there are references to lexical values of OP and REASON
22:55:29
mathrick
it seems to me that you're advocating for how FORMAT does things, whilst also saying that it doesn't have to be that way?
22:56:08
_death
what I'm saying is that Lisp is not C or C++, so people need not judge localization issues with an OUT operator in the same way as with std::cout or the likes
22:56:30
mathrick
_death: OK, so your OUTS boils down to FORMAT and building the format string dynamically under the hood?
22:57:05
aeth
mathrick: You're saying that FORMAT shouldn't be used over alternatives because of localization, while I'm saying that only the part of the string that needs to be localized (which is a tiny, tiny, tiny portion of actual string processing in practice) needs to be localized and can be done so as a reader macro like #_"Operation ~A failed due to ~A~%" which freely composes with just about any way to
22:57:33
mathrick
_death: cool. Can you show me what the extracted localisable text would look like with it?
22:58:25
aeth
although I suppose if it has arguments you have to use the longer form #_("Operation ~A failed due to ~A~%" op reason)
22:58:36
_death
mathrick: like I said.. I didn't use it for localization.. if I would, I'd have some out-operator like I gave in the conversation.. out-operators have the full power of lisp macros
22:59:31
mathrick
aeth: 1) user-facing text is a big part of FORMAT usage in practice 2) not just "the text", but also all the references to the values interpolated into it. Most text show to the user is not static
23:00:20
_death
so the expression, possibly annotated, could be stored and a tool could be written to work with it and let users specify localized descriptions, which can be used at runtime
23:00:57
mathrick
_death: yeah, but I want to know how your "some operator" achieves that feat. Because with FORMAT, it's very easy and a solved problem essentially. The biggest hurdle in Lisp are actually the macros, because it means you don't always see the calls to FORMAT / gettext-like operator to know what all the strings of interest are
23:01:21
aeth
mathrick: (1) By volume, HTML and similar is probably the vast majority of string output and (2) FORMAT does a lot of things that you probably shouldn't do so even if it looks like FORMAT, it isn't. Of course, it could be implemented via FORMAT, but then you'd have to parse all of the ~s first to make sure that it removes almost all of the feature set.
23:01:23
mathrick
with your "some operator", I don't even have a sketch of how it could possibly work
23:02:34
aeth
In principle, _death's s-expression based translation macro faces the same problem that translations in a FORMAT-style string face: the word order can change from the English.
23:02:54
aeth
Perhaps the only valid way to write our trivial example in another language is (reason op) not (op reason)
23:02:58
mathrick
aeth: actually, aside from the English-specific stuff like plurals, the things in FORMAT that seem like it's too much are good for localisation. Because it means you can keep more of it together, in the context, to generate something that will actually read OK in the target language
23:03:17
_death
aeth: like I said, it's in fact easier because the arguments are named and not positional
23:06:50
mathrick
that sounds suspiciously like "cout is better because it's type-safe!" (whatever that means). I'll believe it if you can show me even an outline of what that would actually look like for even the simplest example here
23:07:03
_death
it comes down to the fact that when you want localization, a format control string is a useful hack, not an actual thing that was designed for localization
23:08:20
mathrick
but the thing is, if you want a solution that's good for localisation, format control strings are in fact just about the best design you can come up with. They weren't written with localisation in mind, but they are still more or less the best practical solution anyone's come up with
23:08:46
_death
well, again.. std::cout does not get to analyze its full expression, and C++ programmers don't usually programmatically analyze their source to do that
23:09:28
mathrick
you say that it can be done, but I still have no idea what possible shape it could take
23:10:27
aeth
mathrick: _death just brought up another feature that absolutely needs to not be available in a translation DSL, ~/
23:10:38
aeth
mathrick: and I'm sure if this conversation goes on long enough we'll have a very long list of FORMAT features not to include
23:11:09
aeth
best just to whitelist it and stick to ~A and ~% for now. Especially since you can just compose (at the cost of efficiency) stuff like ~F
23:11:14
_death
if I'll need to write a localization operator sometime, I'll let you know if you're around ;)
23:12:48
mathrick
_death: sure, I'd love to see it! If you can extract the info in a way that can be fed back into the code at runtime, that would be amazing and indeed more powerful than FORMAT
23:16:10
_death
sometimes I think such requests for localizable output should just be (some-identifier :x 42 :y 123 :z 165)
23:16:50
_death
then you don't need to hardcore some message in the code (by default you can even just print the list :)
23:18:51
aeth
That's easy, all you need to do is translate "Guru Meditation" and then everything else is just a bunch of hex
23:20:15
Alfr
_death, just say, it's printed output, not something you could reasonably expect to be useful after reading.
23:20:39
mathrick
_death: you'd still need to come up with a way to get the values into the string, which needs to happen through some sort of code
23:21:16
_death
you can have some-identifier associated with (:language english :format "~A+~A=~A") or (:language english :out (:x "+" :y "=" :z)) .. in both cases these are interpreted (or compiled) at runtime
23:21:29
mathrick
for all its problems, gettext gets it right: translators just get more or less inert text to work with
23:22:50
mathrick
but if SOME-IDENTIFIER needs to know itself how to get the values into its text, then you end up with Win9x style of localisation, where each language was a separate set of DLLs, and they were not compatible with each other
23:23:51
Alfr
mathrick, I'm not certain it's a good idea to give translators only "inert" text; sometimes you really want to switch your arguments around as a translator.
23:25:22
aeth
assuming you're in COMMON-LISP-USER and you use some TO-UTF8 function, then: (format nil "GURU MEDITATION ~{~A:~A~}" (mapcar (lambda (string) (format nil "~{~2,'0X~}" (coerce (to-utf8 string) 'list))) (list (package-name (symbol-package 'foo)) (symbol-name 'foo))))
23:27:46
_death
mathrick: I didn't mean some-identifier is an operator.. it's just data that's passed to the request for localized output, like (out (:l some-identifier :x 42 ...))
23:27:51
mathrick
aeth: heh, that's a great replacement for "Are you sure you want to close file 'foo.txt' without saving? It was last saved 2 hours 45 minutes ago and has been edited 17 times since"
23:28:38
mathrick
_death: so it would have to be interpreted by some sort of string-specific interpreter afterwards, no?
23:29:14
mathrick
which is pretty much what FORMAT / printf are. A specialised interpreter for a very limited (or not so much in the case of FORMAT) embedded language
23:30:01
mathrick
_death: for the same reason aeth wants to exclude ~/ from the language understood by the localisation, since it can open you up for arbitrary code injection
23:31:52
_death
well, I don't immediately see why it needs to protect against "arbitrary code injection" or be limited.. but it could if you wanted to constrain it that way
23:33:07
mathrick
because localisation shouldn't crash your code and allow people to steal credit cards :)
23:33:45
mathrick
so if we're talking localisable text that you ship off to people who aren't necessarily programmers, then it's a useful/necessary constraint to have
23:33:51
_death
if you trust it enough to display something to the user, it is possible that such trust extend to running code
23:34:15
mathrick
that is actually already the case with gettext() in C, it can crash if the localisation messes up the arguments
23:34:44
_death
if you just have a translator wiki that anyone can edit.. sure, probably better to constrain :)
23:34:46
Alfr
mathrick, if you localize and ship yourself, then the format string isn't of concern. (Unless you're malicious.)
23:36:23
aeth
mathrick: sure, you can add seconds since last edit and the number 17 to the end, space separated, too :-p
23:40:45
mathrick
Alfr: that's again a lot like the win9x way, which wasn't great. I like the fact that I can install arbitrary languages for my apps through apt and it just works. Localisations are not of trivial size once you're starting to talk 95+ different languages for something the size of Firefox or Libreoffice
23:41:38
_death
anyway, it reminded me of old parmenides (a project from 1990) localization approach.. it uses format control strings too.. https://github.com/death/Parmenides/blob/master/src/frk/fr-messages-esp.lisp
23:43:05
Alfr
mathrick, not that size itself is really of concern, producing the text is cost prohibitive enough that you won't do that.
23:44:45
Alfr
mathrick, assuming the king of information you want to output is fixed, associating with every tag a permutation will get you very far already.
23:47:15
mathrick
_death: heh, yeah. FORMAT certainly wasn't made *for* localisation, but it does work for it if you stay away from ~:R and its ilk
23:47:45
mathrick
oh man, I forgot that FORMAT can not only do Roman numerals, but also old Roman numerals
23:50:59
_death
anyway, localized output is not the only output a program my generate.. for example I used OUT to generate (non-lisp) source code in adhoc semi-structured way
23:54:10
_death
some years ago I talked about it in this channel and pasted https://gist.github.com/death/4e273d14e671a3c64f3be10cab2aa1b2