shiro
SaaS back-end dev, database engine, compiler/interpreter/DSL.
Most productive with Common Lisp, Clojure, Scheme, Go, C, C++.
Past projects: https://practical-scheme.net/ShiroKawai.pdf
Github: https://github.com/shirok
Some may know me as the author of Gauche Scheme https://practical-scheme.net/gauche/, or as a Japanese translator of Paul Graham's "Hackers and Painters", Conrad Barski's "Land of Lisp", and Stuart Halloway's "Programming Clojure".
But yeah, I'm terrible with rhythm. I'm curious that there are different types of brain to perceive the music.
The idea---not just to think about programs per se, but to think about abstract properties and operations of certain category of programs and to be able to actually write it down---gave the exponential power to you. Lisp was the first such tool as a programming language that allowed you to break the ceiling of first-order thinking.
Nowadays there are other languages that provide meta-level tools. And once you get the idea, the language doesn't really matter; you can apply it to any language, albeit you may have to build scaffolds around it to compensate lack of language support (Cf. Greenspun's tenth rule). So, if you already got the idea through other languages, it's good for you. You'll see Lisp as an arcane prototype of modern languages.
But note: since Lisp can write meta-level description in the same language as the base language, you can top up this ladder infinitely, meaning you can think of program-generating-program, then program-generating-program-generating-program, and so on, using a single set of tools. It is indeed powerful but somewhat crude, so modern advanced languages intentionally restrict that aspect and just try to cover the sweet spot. Even if you're familiar with metaprogramming, it may still be worth to play with Lisp to see what (meta)*programming is like, to know its pros and cons, and to get the idea that might come handy someday when single-level metaprogramming isn't enough.
I suspect this strength of Lisp could have undermined improvement of Lisp compilers in long term. That is, the fact that programmers can customize the language to run efficiently for their applications put less pressure to make the existing compiler better, compared to the languages that don't give programmers such flexibility.
I've worked on performance-sensitive commercial Common Lisp applications. We employed heavy macrology so that optimal instructions were generated in the performance critical regions. Effectively, it was reimplementing part of the compiler to deal with out domain-specific meta information such as parameterized types. It worked, but came with the cost of maintenance--as if we were maintaining another layer of the compiler. It was a burden. I'm sure there have been such effort spent in many other places and eventually abandoned.
Meanwhile, languages that give less power to the programmers, put lots of efforts to improving the compiler and/or the language that work more effectively with the improved compiler, and over a few decades, they have quite sophisticated compilers.
I still mostly use Lisp-family languages at work, but sometimes wonder if the power could have adverse effect in long term.
We couldn't switch to formula because after the initial few weeks my son refused to take it. We tried various ways but couldn't make it work except breastfeeding. If we could, I wouldn't have minded preparing formula, considering how much work my wife has done for pregnancy and delivery.
Most notable one is conflation of vowel [イ] and [エ]. Often both are denoted with "i", sometimes one of them is "yi". In modern American English the sound /i/ falls in middle of Japanese イ and エ. I'm not sure in this case that it's because of that, or Ryukyu dialect had shifted vowels.
セ is denoted as "she" (usually "se"). This variation of consonant appears in some Japanese dialect.
ヒ is denoted as "fi". Might stem from old Japanese pronunciation.
Curiously, ヰ is denoted as "i" and ヱ is denoted as "yi/ye/e". Usually they are "wi" and "we", but those pronunciations have been lost in modern Japanese.
In the ADHD case, it seems about the way of processing stimuli. They (or at least my kid) need a sort of synchronization stimulus (or, outside distrubance, in the way that disturbance suppresses the divergence of hypersensitive systems) to keep his mind on rail. If no such stream of stimuli is provided, he must create one by his own. I try to make him find and build his own toolset to work with. (One of the activities, for example, is tapping along metronome while doing other tasks.)
[Edit] I see your comment in other thread that you refer to Zen meditation. I've learned meditation and I agree on its benefits, but it can't be applied to the current discussion of school setting---"sitting still listening lectures" and "sitting still meditating" are very different activities. The latter would certainly develop the ability of the former, but I'm dubious about just forcing the former.
In his school, the classroom is a kind of free style and although he has assigned desk he can choose other places to work on his task, which seems to help him a lot. (It's Montessori, so most of the time each kid works independently according to his/her own study plan.)
Your explanation of 'ga'/'wa' is spot on as far as I can understand as a layman of native speaker with standard Japanese grammar education in Japan but no advanced linguistic degree.
I'd say that, because 'wa' emphasizes the introduced subject as the center of interest, it isn't used in the subordinate clause.
Tanaka ga nihon ni itta hi. (The day Tanaka went to Japan) ; ok - the interest is on 'hi'
Tanaka wa nihon ni itta hi. ; invalid
Tarou ga Noriko wo mita toshokan
The particle "ga" and "wa" both introduce a topic. But in a phrase to explain a noun, we use "ga" exclusively. Your main point still holds, in a sense that "Tarou ga Noriko wo mita" is a valid sentence. But to be precise, "mita" in those two sentences are different conjugated forms; it just happens that two conjugated forms are the same in the verb "miru" (to see).
IMHO, it's ok to lift answers from SO as long as 1) you know it's a shortcut and 2) when necessary you can trace back the history to the origin so that you can know the original frame the technique was invented and how it was modified along the line. A good solution in a certain context might not be optimal for your context, even though it has been improved by many.
The second point requires a certain level of skill---able to search and read CS papers and implement by yourself or incorporate the ideas into your domain. Without having proper training, it's difficult to acquire that kind of skill solely from skimming SO and alike. But I'd like to assume that graduate-level CS course do give such skill, and by experience you can hone it.
I do note that some answers in SO are pretty decent, with references to the original papers. When I answer online questions I try to do the same as much as possible within my ability and knowledge.
[1] This illustration depicts the difference: https://twitter.com/_KE_N_/status/295134747603107840/photo/1 Calculate the inertia of the water. Also mind the actual tsunami carries lots of debris so you can't easily swim in it.
A language designer may limit the power of abstraction intentionally; maybe the abstraction taxes performance too much; or maybe too much power scares the target users. That's a plausible choice, but that also turns some users away.
I was in 3DGC productions as well. A 3D artist can be just a worker to materialize director's vision precisely as he's told, but they can also contribute the creative process to carry out the greater work.
Once, oxygen was toxic to most of the species on earth, when some algae started producing it by photosynthesis. If Earth fought back, we'd had very different species by now.
With TCO, you start seeing functions as composable code blocks rather than something that is called and then return from. State machines are just mutually tail-calling functions---it's not that state machines are written so, but that in your mental model two are the same. And usually a program is humongous multi-layered state machine.
Suppose you want to abstract the part of the loop. You just write a generic version that tail-calls user-passed function. Users can extend the loop freely, even interlacing two independent loops by mutually calling them (which can't be done with callback model without consuming stack frames).
It's really a matter of style, but for those who get used to think in this way, not having guaranteed TCO is like writing code with one hand tied up in the back.
In the R5RS age, light users looked for try-catch construct in Scheme and could only find dynamic-wind and used it; but they're not the same. Dynamic-wind is a low-level construct that can be used to implement try-catch idiom and other stuff.
Now Scheme has guard, so it's better to use them to write cleanup stuff. It's a bit verbose, though (you have to write cleanup both in the handler and the end of body), so I personally wrote unwind-protect macro on top of it and am using it.
[1] http://gizmodo.com/qwertys-origin-story-is-a-big-fat-lie-493... (This is just a first one I found in English, but Prof. Yasuoka referred in the article publishes quite a few articles about early typewriters in Japanese, in which he lists several evidences that "jamming" wasn't the reason.)
In that regards, I feel that tail call "optimization" is misleading, since it gives an impression that it's some kind of optional bonus the compiler gives to you. The transformation such as your gcc example is, certainly, an "optimization". If you get one, you're lucky; if you don't, fine, you can live with it. Guaranteed tail call elimination is totally different---if you don't have one, you change the way you write code.
(So, the blanket statement of "Lisp" might be inappropriate, for it's only a subset of Lisps that has guaranteed TCO.)
With Lisp it's always been non-trivial to know when a
function is tail-recursive. You can't just blindly count
parens, you have to try to "run" the function in your mind.
To me, Lisp code is just like a tree (well, sometimes DG) and it's statically obvious where the leaves are---no need to "run" the function in mind. But maybe it's just because I get used to it; once you learned, you forget what you saw (or you didn't see) before.One of such patterns is when I have to accumulate multiple kind of things while I zip through the input. Somewhat contrived example: You have a hashtable that maps integer key to a list of strings. You want to scan it just once and build two lists, strings associated with odd keys and strings associated with even keys.
;; populate input
(defvar input (make-hash-table :test 'eql))
(setf (gethash 1 input) '("ichi" "hi"))
(setf (gethash 2 input) '("ni" "fu"))
(setf (gethash 3 input) '("san" "mi"))
(setf (gethash 4 input) '("yon" "shi" "yo"))
(setf (gethash 5 input) '("go" "itsu"))
;; loop
(loop for k being each hash-key in input
when (oddp k) append (gethash k input) into odds
when (evenp k) append (gethash k input) into evens
finally (return (values odds evens)))
; => ("go" "itsu" "san" "mi" "ichi" "hi") and ("yon" "shi" "yo" "ni" "fu")