<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by R. Harvey on Medium]]></title>
        <description><![CDATA[Stories by R. Harvey on Medium]]></description>
        <link>https://medium.com/@r.harvey?source=rss-fbc7a4c81650------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*Hb0ZdCWSNpQMCFAg</url>
            <title>Stories by R. Harvey on Medium</title>
            <link>https://medium.com/@r.harvey?source=rss-fbc7a4c81650------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 12 Jun 2026 23:44:26 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@r.harvey/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[On Taste]]></title>
            <link>https://medium.com/design-bootcamp/on-taste-d4fe921653d9?source=rss-fbc7a4c81650------2</link>
            <guid isPermaLink="false">https://medium.com/p/d4fe921653d9</guid>
            <category><![CDATA[design]]></category>
            <category><![CDATA[beauty]]></category>
            <dc:creator><![CDATA[R. Harvey]]></dc:creator>
            <pubDate>Thu, 19 Feb 2026 15:00:16 GMT</pubDate>
            <atom:updated>2026-02-19T15:00:16.838Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lRm0r0z6tGt9CC8e-Kkvcw.png" /><figcaption><a href="https://www.pexels.com/photo/scenic-mountain-lake-view-with-snowy-peaks-29813169/">Photo by Jason Hu</a></figcaption></figure><p>or why taste is not subjective, featuring a classic Paul Graham essay.</p><p>In the world of AI, taste seems to be becoming increasingly important. Therefore, I think it is necessary to shortly look at the core of taste and beauty and examine the objectivity of taste. Here is my take on the topic, but feel free to let me know what you think as well.</p><h4>The Paradox of Objective Taste</h4><p>Since I was young, I’ve often heard the claim that “taste is subjective.” <br>People state it as if it were an obvious fact, but I’ve never found it fully convincing.</p><p>Take a rose. Its beauty is broadly recognized. People are drawn to lush gardens and instinctively avoid rot and decay. That at least suggests our senses are shaped by evolution to move us toward good and away from bad.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2N2A1HnvEMyp1gjij2pRwg.png" /></figure><p>I agree that small preferences in taste may vary, for example when it comes to fish or cheese. But beauty itself seems to have an objective core. <br>This is also because of the inherent objectivity of joint human subjectivity. <br>When many, at least partially independent, subjective views are aggregated, an objective view emerges.</p><p>However, a paradox exists: for this collective objectivity to be valid, individuals must believe their taste is personal. If people perceive beauty as a fixed standard, they are liable to mimic the consensus rather than form their own judgments, i.e. <em>groupthink</em>. Therefore, the illusion of subjectivity is required to reveal the reality of objective beauty.</p><h4>Taste for Makers — <a href="https://paulgraham.com/taste.html">an essay</a> by Paul Graham</h4><p>For a long time, I felt alone in thinking this way. I didn’t find much support for the idea that beauty could be objective. That changed when I read PG’s essay on taste. It put into words what I had been thinking all along.</p><p>He describes the current confusion about taste very directly:</p><blockquote>“If you mention taste nowadays, a lot of people will tell you that ‘taste is subjective.’ They believe this because it really feels that way to them. When they like something, they have no idea why. It could be because it’s beautiful, or because their mother had one, or because they saw a movie star with one in a magazine, or because they know it’s expensive. Their thoughts are a tangle of unexamined impulses.” — Paul Graham</blockquote><p>Moving beyond the critique of subjectivity, Graham constructs a framework for objective quality, identifying several immutable characteristics of good design, that I strongly agree with:</p><p><strong>Simplicity</strong><br> “When you’re forced to be simple, you’re forced to face the real problem. When you can’t deliver ornament, you have to deliver substance.”</p><p><strong>Timelessness</strong><br> “Aiming at timelessness is a way to make yourself find the best answer: if you can imagine someone surpassing you, you should do it yourself.”</p><p><strong>Suggestiveness</strong><br> “Jane Austen’s novels contain almost no description; instead of telling you how everything looks, she tells her story so well that you envision the scene for yourself.”</p><p><strong>Utility</strong><br> Design must not merely exist; it must fulfill its intended purpose.</p><p><strong>Strangeness and Humor</strong><br> Graham notes that truth often carries an inherent oddity: “Einstein didn’t try to make relativity strange. He tried to make it true, and the truth turned out to be strange.”</p><p>Furthermore, he suggests a link between levity and quality: “Good design may not have to be funny, but it’s hard to imagine something that could be called humorless also being good design.”</p><p><strong>The Illusion of Ease</strong><br> True mastery conceals the effort required to produce it. “If you’re not working hard, you’re probably wasting your time,” Graham asserts, adding that “Great designers make it look easy. Mostly this is an illusion.”</p><p><strong>Symmetry and Nature</strong><br> Finally, he appeals to the ultimate standard: “Nature has had a long time to work on the problem. It’s a good sign when your answer resembles nature’s.”</p><p>I would appreciate, if you leave a clap. But, only if you liked the article. <br>Also, let me know your questions or comments as well.</p><p>Regards,</p><p>Rashid Harvey</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d4fe921653d9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/design-bootcamp/on-taste-d4fe921653d9">On Taste</a> was originally published in <a href="https://medium.com/design-bootcamp">Bootcamp</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Prompt engineering techniques to avoid hallucination in AI agents]]></title>
            <link>https://medium.com/@r.harvey/prompt-engineering-techniques-to-avoid-hallucination-in-ai-agents-1bb61178ef5c?source=rss-fbc7a4c81650------2</link>
            <guid isPermaLink="false">https://medium.com/p/1bb61178ef5c</guid>
            <category><![CDATA[prompt-engineering]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[llm]]></category>
            <dc:creator><![CDATA[R. Harvey]]></dc:creator>
            <pubDate>Mon, 15 Dec 2025 19:01:55 GMT</pubDate>
            <atom:updated>2025-12-15T19:01:55.404Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qy7gK_BEzIGMXbAEwlajmg.webp" /><figcaption><a href="https://pixabay.com/illustrations/woman-face-smoke-ink-nebula-art-6371408/">Pixabay</a></figcaption></figure><p>When building AI agents, one of the biggest problems is hallucinations. But how can we prevent or mitigate them?</p><h3>Hallucination</h3><p>First, we need to understand what hallucinations are and why they occur. Generally, the term ‘hallucination’ refers to an LLM (confidently) making things up. But why does this happen?</p><p>The LLM is trained to give probabilities for each possible next token of a text. For training, a large corpus of data is used, which necessarily contains lots of data from the internet. Therefore, the models generally generate content similar to that which can be found on the internet, e.g. Wikipedia.</p><p>This is where lots of the antipatterns come from. For example, on the internet (and, tragically, in life generally) you will only rarely find someone admitting that they are wrong or that they do not know something. The LLM is therefore trained to just say something rather than say it doesn’t know. Luckily, this has already improved.</p><h4>Fine-tuning and RLHF</h4><p>Fine-tuning is the process of training the model further after its general pretraining (GPT = General Pretrained Transformer). This for example includes following instructions or training for specific domains like legal, math or coding. Recent models have been specifically trained to avoid hallucination using reinforcement learning using human feedback (RLHF)</p><figure><img alt="An image of a person having hallucinations for illustration purposes" src="https://cdn-images-1.medium.com/max/1024/1*9KtuOE5r3juim3YHPP3YPw.png" /><figcaption>Generated using <a href="https://gemini.google.com/share/5c780f00c11e">Nano Banana Pro</a></figcaption></figure><p>The other reason, hallucinations occur is that once a token is generated, the LLM can’t take it back. It must simply continue; accepting the token. This means that one bad token can hijack the whole response. A funny example of this is the seahorse emoji 🌊🐎. Until recently, asking ChatGPT whether a seahorse emoji exists, would send it spiraling, generating all possible related emojis until finally exhausted, admitting defeat.</p><p>For example, see <a href="https://vgel.me/posts/seahorse/">this blog post</a>, which btw also shows that it’s not (just) the LLMs that are hallucinating (Mandela effect), but it also goes into much more technical detail explaining what happens inside the model.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-uM1iCA4CSbNdWFQcPFknw.png" /><figcaption><a href="https://vgel.me/posts/seahorse/">https://vgel.me/posts/seahorse/</a></figcaption></figure><p>This specific problem is now mostly fixed through fine-tuning, reasoning and online search as you can see here:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*tKRcHd2gGV4WjPzvMIyGDA.png" /><figcaption><a href="https://chatgpt.com/s/t_6934c4dcf98c8191addc7794c5c82539">ChatGPT</a></figcaption></figure><h3>The Solution(s)</h3><p>Because fine-tuning is so difficult and the most powerful models aren’t openly available anyway, we are left with prompt engineering, i.e. the harnessing around the LLM. Here are some techniques I found useful:</p><ol><li>A way out</li><li>Preprocessing</li><li>Reasoning</li><li>Post-processing</li></ol><p>I hope this helps</p><h4>1. A way out</h4><p>In my personal anecdotal experience, I have noticed giving the model a way out, i.e. giving it a plausible thing to do if it doesn’t know the answer, already helps. For example, for coding, you could tell it that it’s a junior engineer, and it should ask the senior engineer if it is stuck.</p><p>On the other hand, you can tell the model to only talk about a certain topic it is an expert in and refuse to answer questions regarding any other topic.</p><p>The general prompt engineering rule applies: <br><em>Never give the model a choice, always tell it exactly what to do.</em></p><h4>2. Preprocessing</h4><p>Preprocessing is all about giving the LLM the information it needs to fulfill a task or answer a question. One of the most-widely deployed approaches is retrieval-augmented generation (RAG). Before the LLM is given the query, the query is passed to a retriever, which finds documents from a database that fit the query, i.e. semantic search. These documents are then passed to the LLM together with the query.</p><p>A similar approach is online search, which has the additional benefit of providing real-time data like date, time, weather or news.</p><h4>3. Reasoning</h4><p>Just like a human should think before he speaks, we can use reasoning models which are trained to do just that.</p><p>Reasoning allows the model to</p><ol><li>Formulate a response before “submitting” it,</li><li>Revise any inconsistencies,</li><li>Summarize findings and</li><li>Follow a chain of logical steps, like a mathematical derivation: <br>“this leads to that, which leads to the following”</li></ol><p>This is especially powerful together with retrieval.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IZmJEi88pImDzBD7zUCo2g.png" /><figcaption>Generated using <a href="https://gemini.google.com/share/55ae6e101369">Nano Banana Pro</a></figcaption></figure><p>Even without explicit reasoning models, you can ask the model to first start with the reasoning/explanation and then follow it up with the answer. This avoids the bad token generation problem, where the LLM is only left to justify a wrong answer.</p><p>The more natural way would often be to provide the answer first, e.g.: <br>“Yes, penguins can fly because they are birds.”</p><p>Here, after the “yes” is generated, the LLM can only try to find the best justification, but it can’t naturally say something else. It would rather continue saying something wrong than write something absurd like:</p><blockquote>Can penguins fly?<br>Yes, penguins can’t fly because their wings aren’t made for it.</blockquote><h4>4. Post-processing</h4><p>Depending on the domain, post-processing is more-or-less difficult. The general question to be asked is: Is this answer even plausible?</p><p><strong>Verifiability: </strong>Often checking correctness, is much easier than generating a correct solution. For example, for Sudoku.</p><p>One way to check a generated response is by simply asking the model: <br><em>“Are you sure?</em>” If the model is sure, you can keep the original answer. If it isn’t, there are several ways to go. Best is to try out different approaches like retrying or summarization. You could call this approach <strong><em>reasoning-lite</em></strong>.</p><p>Another approach is to verify the result of one LLM with another. <br>This works even better because the different providers are diverging farther and farther away from another in an attempt to compete. <br>This means they use different model architectures, different training data, different fine-tuning and different human feedback.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gLM3xm9w9f1AZVovlHubTw.webp" /><figcaption><a href="https://marketoonist.com/2025/06/ai-hallucinations-and-reliability.html">“AI Hallucinations and Reliability”</a> by Tom Fishburne @<a href="https://marketoonist.com/">marketoonist.com</a>, June 30, 2025</figcaption></figure><p><a href="https://medium.com/u/ac9d9a35533e">Andrej Karpathy</a> brought this concept to <em>attention </em>and also to a beautiful extreme with his latest contribution: <a href="https://github.com/karpathy/llm-council"><strong>LLM Council</strong></a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kJYz5zHin8PpTNtZJRUnmw.jpeg" /></figure><p>I’ll let him explain in his own words:</p><blockquote><a href="https://github.com/karpathy/llm-council">This repo</a> is a simple, local web app that essentially looks like ChatGPT except it uses <a href="https://openrouter.ai/">OpenRouter</a> to send your query to multiple LLMs, it then asks them to review and rank each other’s work, and finally a Chairman LLM produces the final response.</blockquote><p>I hope this was a useful overview of hallucination, and you can benefit from this in your own work. Please be aware that, due to the attention mechanism of transformers, you can do a lot with prompt engineering. <br><strong>Use this power wisely.</strong></p><p>Signing out,</p><p>Rashid Harvey</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1bb61178ef5c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why building AI teachers is impossible]]></title>
            <link>https://medium.com/design-bootcamp/why-building-ai-teachers-is-impossible-b38a4c257551?source=rss-fbc7a4c81650------2</link>
            <guid isPermaLink="false">https://medium.com/p/b38a4c257551</guid>
            <category><![CDATA[ux]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[teaching]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[R. Harvey]]></dc:creator>
            <pubDate>Fri, 31 Oct 2025 23:44:32 GMT</pubDate>
            <atom:updated>2025-10-31T23:44:32.928Z</atom:updated>
            <content:encoded><![CDATA[<p>or at least nearly impossible</p><figure><img alt="An AI robot fading away in a classroom" src="https://cdn-images-1.medium.com/max/1024/1*oCWaWi6VOZ_0LsgewWLsMw.png" /><figcaption>Generated by Gemini</figcaption></figure><p>For quite some time now, I have been thinking about how to build effective AI teachers. I myself have been a lifelong student and teacher and aim to continue this beautiful <em>effortful struggle</em> — <a href="https://www.youtube.com/watch?v=0xS68sl2D70">as Derek Muller would put it</a>.</p><p>However, as a software developer and AI engineer, I want to believe that it is possible to build AI teachers that are almost indistinguishable from real humans; essentially passing the Turing test for teaching.</p><p>In this article, I will firstly lay out the difficulties, and then continue with my idea for the future of AI tutors and what would be necessary for them to become reality (which will probably be the most interesting). I will leave the assessment of current solutions “as an exercise” to the reader.</p><h4>Definition of Teaching</h4><p>To really understand what we are talking about, we should define it.</p><p>First, let’s look at the definition Google gives for “to teach”:</p><ol><li><a href="https://www.google.com/search?q=impart">impart</a> knowledge to or instruct (someone) as to how to do something.<br>“she taught him to read”</li><li>give information about or instruction in (a subject or skill).<br>“he came one day each week to teach painting”</li><li>work as a teacher.<br>“she teaches at the local high school”</li><li>cause (someone) to learn or understand something by example or experience.<br>“travelling taught me that not everyone shared my beliefs”</li><li>encourage someone to accept (something) as a fact or principle.<br>“the philosophy teaches self-control”</li></ol><p>In summary, I would define teaching like this</p><blockquote>teaching: the transfer of knowledge</blockquote><p>Knowledge can be very simple information like the date of the end of the second World War, or it can be much more complex like driving a car, composing music, solving math equations or speaking a language.</p><p>A predicament for the <strong>transfer</strong> of anything is of course that the thing already exists. The teacher must in some way or form already have the knowledge to be able to teach it.</p><p>Also, teaching cannot be separated from learning. There is no teaching without learning, and no teacher without a student. Teaching is a two-way process, which makes it so intimate and fulfilling.</p><h4>Fundamental Difficulties</h4><ol><li>There is very little (textual) data available for teaching</li><li>Teaching is very difficult to verify: “Verifier’s law” by Jason Wei</li><li>Teaching is even difficult for humans. Most humans don’t know how to effectively teach. Teaching is a rare skill.</li></ol><h3>Data Availability</h3><p>Firstly, most teaching still happens in a — <strong>non-digital</strong> — classroom, which are not recorded.</p><p>Secondly, published teaching materials like books, articles, courses and lectures are designed in a completely different manner than <strong>one-on-one tutoring</strong>. <em>This is a general problem for conversational AI.</em></p><p>Finally, teaching requires a lot of <strong>non-verbal cues</strong>, because the teacher needs to know what the student understood and understanding is mostly communicated non-verbally (nodding, frowning, sighing, etc.)</p><h3>Verifiability</h3><blockquote>“The ability to train AI to solve a task is proportional to how easily verifiable the task is. Any solvable, easily verifiable task will eventually be conquered by AI.”<br> — Verifier’s law by Jason Wei <br><a href="https://www.youtube.com/watch?v=b6Doq2fz81U">https://www.youtube.com/watch?v=b6Doq2fz81U</a></blockquote><p>He also gives 5 criteria for verifiability</p><ol><li>Objective Truth</li><li>Speed</li><li>Scalability</li><li>Low noise</li><li>Continuous reward</li></ol><p>For learning, the ultimate objective is, as we determined above, the transfer of knowledge (to a human). How are you supposed to verify the transfer of knowledge objectively, fast and scalable? Teaching has extremely high noise (i.e. randomness and entropy). However, I believe learning can be assessed on a scale quite well.</p><p>Takeaway ⇒ AI could use tests or exams not only as a measurement of the student&#39;s capabilities, but possibly also as an assessment of his own?</p><p>Other idea: what about neural imaging to quickly verify learning effects?</p><h3>General Difficulty</h3><p>Teaching is even difficult for humans. How many of your teachers would you say were good teachers? And those people studied education for at least two to five years and probably had decades of full-time experience.</p><p>I believe that teaching is a skill that does not come naturally to the human. It requires an understanding of not only your own cognition, but also that of someone else. <em>Almost like cognitive empathy</em>.</p><p>Have you ever heard your mother (or someone close to you) say something like: “I already told you this a hundred times, and you still do it.” If your mother was a natural teacher, she would understand that she is doing something wrong if she is telling you something so many times without any effect on your behaviour. Clearly, she should try an alternative approach.</p><p>The fact that she, at least subconsciously, believes that, if she just tells you often enough, you will change, is kinda mind-blowing.</p><h3>Summary</h3><p>Teaching is difficult for humans, not digital, and it is difficult to get or synthesize data. This makes it impossible for AI to master.</p><h3>Requirements for good teaching AI</h3><p>But let’s at least try to find some requirements for good AI teachers so that we can get closer to finding it.</p><h4>Driving</h4><p>What LLMs are amazing at is providing structured and, mostly, accurate information. This has already made self-driven, intrinsically-motivated learning much easier, but I wouldn’t call it teaching, just like Wikipedia isn’t teaching anything. Because teaching is an active process, not a passive or, in the case of LLMs, reactive process.</p><p>A teacher needs to drive the student, not the other way around. Current LLMs sycophancy makes them too easy to push-over and simply not assertive enough. <br>A teacher needs to have a plan of what knowledge he wants to teach, and he needs to know how he can teach it effectively.<br>A teacher must often withhold knowledge while providing the tools necessary to acquire it autonomously. A teacher must be able and ready to push a student beyond his limits to enable real growth.</p><p><strong>A side note:</strong> LLMs have actually massively hurt education by making it much easier to solve exercises without doing the hard work that is necessary for learning. Think of physical exercise:</p><blockquote>“You wouldn’t take a fork-lift to the gym. The point is to make the reps.”<br> — Charlie Gedeon in <a href="https://www.youtube.com/watch?v=m8WomdCLBqE">Is AI making us dumber? Maybe.</a> <br>TEDxSherbrooke Street West, September 2025</blockquote><h4>Memory and Personalization</h4><p>One recent improvement of AI agents is memory. Good memory is essential for teaching.</p><p>Firstly, the student’s knowledge must be known to be able to expand it. Initially building and expanding a mental model of the student’s knowledge is in itself a highly difficult problem. I think, it requires the teacher to already have his own knowledge mapped out.</p><p>Secondly, every student has his own personal quirks, strengths and weaknesses, difficulties, motivations and interests. Without a sufficient personalization, it is unreasonable to expect teaching to be equally good for every student. The teacher must be able to adapt to the personal needs of each student to an extreme level.</p><p>One of the reasons I stopped using Duolingo, for example, was that it would constantly give me exercises for the Arabic alphabet even though I didn’t need it, so it felt like a chore. It was so annoying that I stopped learning Arabic and later stopped using Duolingo altogether. <em>I couldn’t bare it.</em></p><h4>Personality</h4><p>AI teachers must not only be <strong>personalized</strong> but also <strong>personal</strong>. This is probably not possible at the moment, but is definitely being worked on.</p><p>Personality means:</p><ul><li>Bias, quirks and weirdness</li><li>Real-world stories and anecdotes</li><li>Relationship and accountability</li><li>Humour and emotion</li></ul><p>Teaching requires holding the student accountable and constantly motivating the student, especially if the student is not intrinsically motivated to learn. You need to show the student the fruits of his work and the light at the end of the tunnel.</p><p>A teacher needs to incite fascination, inspiration and curiosity, break the student’s mind, put their head upside down and push them to their limits, be funny and humorous, and tell stories, give parables and mnemonics.</p><p>For example, I can still remember the Pythagorean theorem and the simple integer solution 3²+ 4² = 5² from the story of the Egyptians measuring their fields along the Nile. The more vivid the story and the more imagination is involved, the better.</p><h4>Multi-modal I/O</h4><p>Everyone is talking about the next AI UI. Everyone knows that it isn’t a chat. But what is it?</p><ul><li><strong>Chat</strong> is good because it’s relatively unambiguous and easy to use. Also, it’s the native I/O for LLMs currently.</li><li><strong>Voice</strong> is necessary because it is much faster input (3–4x) and the native I/O of humans. However, current voice approaches are simply lacking. Currently, the user input is converted to text then fed into an LLM and then converted back to voice. This throws away so much context.</li><li><strong>Video </strong>would be optimal to also read gestures, facial expressions (AI is better at reading micro-expressions than humans due to its speed) and body language. But it probably is a UX disaster because the cost of privacy and compute outweighs the benefit too much. <br>Note: I once gave a student no-camera lessons via Zoom, and we both quickly noticed how difficult it was to give lessons without the instant visual feedback video provides.</li><li>One of the more interesting ideas is <strong>graphical and manual</strong> I/O. A digital playground or whiteboard like <a href="https://miro.com/">Miro</a> or <a href="https://www.tldraw.com/">tldraw</a> that both the teacher and the student can use to sketch ideas, underline stories and show examples would have an enormous benefit. Or think interactive animations and simulations like <a href="https://brilliant.org/">Brilliant</a>, but spontaneously generated.</li></ul><p>Please note that people learn best with multimodal learning approaches: visual + auditory + manual, as Veritasium shows in their video <br><a href="https://www.youtube.com/watch?v=rhgwIhB58PA">The Biggest Myth In Education</a>.</p><h4>Learning Teaching</h4><p>A truly successful AI teacher must be able to dynamically learn (globally) how to teach. The difficult thing is measuring and verifying teaching, as mentioned above. The next step is fine-tuning and reinforcement learning.</p><h3>Technical Challenges</h3><p>Let’s look at the technical challenges these requirements pose and some ideas on how they can be solved.</p><blockquote>[AI agents] just don’t work. <br>They don’t have enough intelligence. <br>They are not multi-modal enough. <br>They can’t do computer use […].<br>They don’t have continual learning. <br>You can’t just tell them something and they’ll remember it. […]<br>It will take about a decade to work through all of those issues. <br> — Andrej Karpathy in <a href="https://www.youtube.com/watch?v=BlVnGXEzFow">an interview with Dwarkesh</a> from October 2025.</blockquote><h4>Andrej’s Problems</h4><ol><li>Reasoning</li><li>Multi-Modality</li><li>Tool-use</li><li>Continual learning</li><li>Memory</li></ol><p>These are all things that are already being worked on quite heavily by different people, but they are still in their infancy, and so I think there will be concrete work-arounds necessary.</p><h4>Work-around 1: prompt engineering</h4><p>Don’t let the AI reason about teaching. Teach it yourself using effective and concise prompt engineering. Prompt engineering is quite under-rated and can even avoid hallucinations and sycophany in my experience.</p><h4>Work-around 2: fine-tuning and RL(HF)</h4><p>Fine-tuning with (Q)LoRA and reinforcement learning (from human feedback) isn’t perfect, but works, and can be used to replace, or at least augment, real continual learning, reasoning, prompt engineering and enhance tool-use capabilities.</p><h4>Work-around 3: memory</h4><p>Just like with prompt engineering, I believe, memory management shouldn’t be left to the agent, keyword: <a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents"><strong>context engineering</strong></a>.</p><p>Firstly, the agent should remember things from previous conversations. There are two state-of-the-art methods:</p><ol><li>The agent can retrieve from previous conversations (RAG)</li><li>After each session, the agent summarizes the contents. <br>The log of summaries are fed into the context.</li></ol><p>Secondly, there probably needs to also be a <strong>more structured</strong> <strong>memory</strong>. <br><em>This needs to be human-designed </em>and should be customized to the subject and learning object. For example, this could be a record on different concepts that should be learnt.</p><h4>Generally good ideas</h4><p>Some other ideas and concepts will not hurt as well:</p><ul><li>pre-processing</li><li>post-processing and plausability filtering</li><li>sub-agents with seperate contexts</li></ul><p>The application needs to be smart. Read <a href="https://medium.com/design-bootcamp/smart-algorithms-5f76ad1e8945">my article on smart algorithms</a> to understand what this means. In short:</p><blockquote><em>Smart Algorithms are algorithms that have maximally valid/desirable output with minimally valid/desirable input.<br>You will need a lot of ifs and elses and error handling.</em></blockquote><h4>Latency</h4><p>Another practical challenge is latency. A set of self-deployed self-trained models close to the user are a good idea. The other way to reach lower latencies is adaptive reasoning: less reasoning and smaller models for easier problems.</p><h3>Bonus: Useful experiments</h3><p>Here are some useful (technical) experiments you can try to test whether AI tutors can be good at the moment. I’ll keep this section short.</p><ul><li>Can AI draw things on a whiteboard. Let it draw a right-angled triangle. Then let it explain the pythagorean theorem visually. <br>Finally, ask it to assess it’s own work.</li><li>Design a memory layout to make the AI agent record specific information like the user’s name. Build tests. How reliably does it work?</li><li>Fine-tune a small AI model that was trained before tool-use to use a specific tool you designed. Test again!</li></ul><p>Wanna build any of this? Hit me up at r.harvey[at]outlook.de</p><p>Signing out,</p><p>Rashid Harvey</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b38a4c257551" width="1" height="1" alt=""><hr><p><a href="https://medium.com/design-bootcamp/why-building-ai-teachers-is-impossible-b38a4c257551">Why building AI teachers is impossible</a> was originally published in <a href="https://medium.com/design-bootcamp">Bootcamp</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A practical lesson on combinatorics]]></title>
            <link>https://medium.com/@r.harvey/a-practical-lesson-on-combinatorics-f87642ce2c3c?source=rss-fbc7a4c81650------2</link>
            <guid isPermaLink="false">https://medium.com/p/f87642ce2c3c</guid>
            <category><![CDATA[math]]></category>
            <category><![CDATA[mathematics]]></category>
            <category><![CDATA[education]]></category>
            <category><![CDATA[combinatorics]]></category>
            <dc:creator><![CDATA[R. Harvey]]></dc:creator>
            <pubDate>Thu, 30 Oct 2025 18:44:23 GMT</pubDate>
            <atom:updated>2025-10-30T18:44:23.195Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dQDeV0ItkfL_FtbIzgP5VA.png" /><figcaption>The Solution</figcaption></figure><p>Last Christmas, I wrote about an interesting Secret Santa combinatorics problem. The question is: If you randomly assign who gives a gift to whom, how likely is it that two people gift each other? Everyone needs a gift and must give a gift, and no one can gift themselves.</p><p>In mathematical terms, to get the likelihood we are looking for, we can divide the number of positive events, two people give each other a gift, by the total number of possible events. Now we just need to know what these numbers are.</p><p>In the last article, we came up with not less than two recursive definitions for the total number of possible gift arrangements.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/658/1*kVFYn3bWp0ggmISPP9WJ8A.png" /><figcaption>Recursive terms for the total number of gift arrangement</figcaption></figure><p>However, I wasn’t able to provide an exact or a recursive definition for the number of such arrangements that do not contain a pair.</p><h4>So what now?</h4><p>There must be some use to going to uni, I thought, and so I asked people about the problem. A professor told me, after thinking for a few seconds, that I should put the first members of the series on <a href="http://oeis.org">oeis.org</a>, as surely there must have been someone who already thought of this before.</p><p>Sure enough, of course there has. <a href="http://oeis.org">oeis.org</a> is the “Online Encyclopedia of Integer Series”, and I found both the series we were looking for:</p><p>Firstly, the number of all possible arrangements is, funnily, called <a href="https://oeis.org/A000166">derangements: the permutations of n elements without fixed-points</a>. <br>Fun Fact from the encyclopedia: Euler proved both recurrences in 1809.</p><p>Secondly, the number of positive events are the number of <a href="https://oeis.org/A158243">derangements with at least one two-cycle</a>. The encyclopedia even gives <a href="https://oeis.org/A158243/b158243.txt">the first 200 elements</a> of the series!</p><p>However, the most interesting are the functional approximations, which are also given:</p><p>Number of derangements: <em>a(n) = round(n!/e)</em><br>With at least one two-cycle: <em>a(n) ~ n! * (exp(-1)-exp(-3/2))</em></p><p>If we put this together, we get the following analytical approximation</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pr6IyEJri3jyJfW_qnLWMg.png" /><figcaption>Analytical approximation of the wanted likelihood</figcaption></figure><p>Which is almost exactly the result we got last time already, but now we have it analytically and can definitely say that the probability will revolve around this value for any group size <em>n</em>.</p><pre>3 : 0 / 2 -&gt; 0.0<br>4 : 3 / 9 -&gt; 0.3333333333333333<br>5 : 20 / 44 -&gt; 0.45454545454545453<br>6 : 105 / 265 -&gt; 0.39622641509433965<br>7 : 714 / 1854 -&gt; 0.3851132686084142<br>8 : 5845 / 14833 -&gt; 0.39405379896177445<br>9 : 52632 / 133496 -&gt; 0.39425900401510156<br>10 : 525105 / 1334961 -&gt; 0.39334856973349785<br>11 : 5777090 / 14684570 -&gt; 0.39341226879643054<br>12 : 71555121 / 176214841 -&gt; 0.40606750597130464<br>13 : 901364100 / 2290792932 -&gt; 0.3934725340771219<br>14 : 12618959353 / 32071101049 -&gt; 0.3934682296600936<br>15 : 189284859750 / 481066515734 -&gt; 0.393469205523884<br>16 : 3069424181265 / 7697064251745 -&gt; 0.39877855775585236</pre><p>Exact results up to <em>n=16</em></p><h4>The lessons</h4><p>I learned quite a lot from this short mathematical adventure, that you need to ask the right people if you want to be successful. The thing is: Even though I knew about the encyclopedia, I didn’t think of using it. It might seem like a random coincidence that the professor gave me the idea, but in some sense it wasn’t.</p><p>Now, I also understand more that you need to learn where and how to search for what you are looking for. The internet has undoubtedly made this much easier.</p><p>Signing out,<br>Rashid Harvey</p><p>PS: The LaTeX renders were made using <a href="https://www.quicklatex.com/">quicklatex.com</a>. Give it a try!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f87642ce2c3c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using Agentic AI Code Editors]]></title>
            <link>https://medium.com/design-bootcamp/using-agentic-ai-code-editors-73d2494deea2?source=rss-fbc7a4c81650------2</link>
            <guid isPermaLink="false">https://medium.com/p/73d2494deea2</guid>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[agents]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[R. Harvey]]></dc:creator>
            <pubDate>Fri, 21 Feb 2025 11:04:42 GMT</pubDate>
            <atom:updated>2025-02-28T23:36:26.495Z</atom:updated>
            <content:encoded><![CDATA[<h3>Using Agentic AI Editors</h3><p>I tried StackBlitz’s <a href="http://bolt.new">bolt</a>, <a href="https://lovable.dev/">Lovable</a>, and <a href="https://www.trae.ai/">TraeAI</a> by TikTok. In general, I was really impressed. However, there are still some things missing: After the AI did 80% of the work, the final 20% were not too enjoyable.</p><figure><img alt="Do AI code agents like bolt, Lovable and Trae work?" src="https://cdn-images-1.medium.com/max/1024/1*krj-hyLfyhaA5KlTuzvMFg.png" /></figure><p>Agentic AI editors consist of a chat where you tell the AI what you want to build, and the bot plans and edits the source files to reflect your wish. While bolt, Lovable and others are purely for web development, Trae, Cursor and others are IDEs or IDE extensions that run locally and can do essentially anything:</p><blockquote>What do you want to build? — <a href="http://bolt.new">bolt.new</a><br>Lovable is your superhuman full stack engineer. — <a href="http://lovable.dev">lovable.dev</a><br>Trae is an adaptive AI IDE that transforms how you work, <br>collaborating with you to run faster. — <a href="http://trae.ai">trae.ai</a></blockquote><p>Let me start chronologically with my experience using bolt.</p><h3>1. Building a mobile public traffic display with bolt</h3><p>I live in Berlin and we recently moved. This came with a lot of change, for example in our transport connections. My mom told me that she would love a way to know which train or bus was coming when, from the comfort of her home, to decide when to leave and which mode of transport to choose. This can be done using either <strong>Google Maps</strong> or the <strong>official BVG</strong> (public transport provider in Berlin) <strong>app</strong>. However, they were both not very comfortable: Google Maps requires you to input where you want to go and then shows you all possible routes. It almost does <strong>too </strong>much when you already know <strong>how </strong>you can reach your destination. The official app, on the other hand, was not viable either, because it can only show departures for a single station at the same time.</p><p>So, I decided to build a web app that I could deploy for free on <a href="https://www.netlify.com/">Netlify</a> and that my mom could save as a shortcut to her home screen. But really, I had been waiting for an excuse to try out the shiny new thing that <a href="https://www.youtube.com/@Fireship">Fireship</a> had “advertised” in one of his videos: <a href="https://bolt.new">bolt</a> by <a href="https://stackblitz.com/">StackBlitz</a>. I had already used StackBlitz and bolt was free.</p><p><a href="https://bvg-view.netlify.app/">The app “I” built</a> allows the user to search for and select several stations. All departures from all stations are then shown together in a list sorted by soonest departure time. The departure times are updated regularly (~ every 10 seconds).</p><p>I wrote quite a detailed prompt, and quite quickly the AI had already built a working web app using <a href="https://nextjs.org/">Next.js</a>. Initially and repeatedly, it did throw many errors, but I basically just pressed the “fix” button every time and the AI figures out what it has to do on its own by reading the error message. Interestingly, it even got the BVG API calls correctly, which exceeded my expectations. The frontend was written with <a href="https://react.dev/">React</a> and <a href="https://tailwindcss.com/">Tailwind</a> and as far as I can tell consisted of good and readable code. After about two hours of tweaks and adjustments, I had a working and deployed live web app. Of those two hours, I was only actively doing something a quarter of the time, and I could work on other things simultaneously. <a href="http://bvg-view.netlify.app">Deploying</a> and <a href="https://github.com/theRealProHacker/bvg-view">transfer to GitHub</a> was also just the click of a button. Try it out if you live in Berlin.</p><p><a href="https://bvg-view.netlify.app/">BVG View - digital departure board for Berlin&#39;s public transport</a></p><p>However, I have to say that I didn’t really choose to stop after two hours. Suddenly, without prior notice, my tokens were empty, and I was told to wait a day. Luckily, the app was already in a state where I could proudly present it to my mom, and she was satisfied.</p><p>After a few days, I had tested the app quite extensively (mostly on my mobile) and immediately noticed that the app was not optimized for mobile and constantly overflowed the screen width. Also, none of the very important SEO/meta things had been handled. I had to create different icons, the title, and description as well as the card image you can see in the embed above all by myself. As you can imagine, fussing with AI-generated Tailwind classes and Next.js configurations was quite tedious, but with the help of the internet I was able to solve most problems. It did, however, take quite a few hours to resolve all final issues and reach a point where I could proudly set the visibility of the GitHub to public.</p><p>After some time, I got an email by GitHub’s dependency bot. Apparently, the Next.js and <a href="https://postcss.org/">PostCSS</a> packages were outdated and had known vulnerabilities. While it was easy to fix in this particular case, it exposes a fundamental problem with these AI agents: They cannot operate effectively in an always changing, fast-paced world if their training data is not very recent — which is not really possible when you take the huge amounts of data into consideration that are required to train such a capable LLM.</p><h3>2. Building a chess site with lovable</h3><p>A friend of mine sent me a link to Lovable, and so I decided to give it a try, after I had used up my bolt tokens building the last project.</p><p>For a startup idea, I wanted to build a landing page: It’s all about the next-generation, all-encompassing chess learning app. My original codename was OreoChess (because Oreos are black and white, just like the players in chess). However, I didn’t want to risk a trademark infringement, and so I decided to pivot to <a href="https://cookies.lovable.app/">Chess Cookies</a>.</p><p>I love the cookie theme Lovable created. Integrating with <a href="https://supabase.com/">Supabase</a> (“free” and painless backend) and <a href="https://stripe.com/">Stripe</a> (ubiquitious payment provider) was almost too easy. I was quite satisfied, for example it also let me add legal pages for Germany, specify the fancy <a href="https://fonts.google.com/specimen/Cookie">Cookie font</a> or upload my own cookie icon. However, I felt like the underlying AI model employed by bolt was a bit better. For example, with Lovable the hero title is cut off at the bottom, even though I tried three times to fix this. Trying to get the AI to build a working chess board failed horribly.</p><p><a href="https://cookies.lovable.app/">Chess Cookies</a></p><p>What do you think? Tell me in the comments.</p><h3>Bolt vs Lovable</h3><p>bolt and Lovable feel the same, look the same and do the same. <br><strong>They are the same.</strong></p><ul><li>Both have simple GitHub logins</li><li>Both make it easy to integrate Supabase, Lovable also supports Stripe</li><li>Both make egress and deployment relatively easy. Moving my Lovable app to Netlify was a bit more difficult and required some debugging.</li><li>The advantage of StackBlitz is that it offers a lot of starter templates that ensure that everything runs in the beginning. On the other hand, Lovable is much better at creating a community (feeling) with their carefully crafted emails and their <a href="https://launched.lovable.app/">showcase</a>.</li><li>Regarding the quality. Lovable and bolt both work well, but in my experience they still have problems with responsiveness (Lovable with very large and bolt with very small resolutions).</li><li>For perfect results, a lot of manual work is required. This is amplified by the fact that the tokens quickly run out when you try to get the AI to change little things.</li><li>Polish is not the AIs strength.</li><li>Finally, both bolt and Lovable have a quite limited free tier.</li></ul><p>For example, this is a sitemap that Lovable generated. <br>The last modified date: 2023–07–01!</p><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<br>&lt;urlset xmlns=&quot;http://www.sitemaps.org/schemas/sitemap/0.9&quot;&gt;<br>  &lt;url&gt;<br>    &lt;loc&gt;https://your-domain.com/&lt;/loc&gt;<br>    &lt;lastmod&gt;2023-07-01&lt;/lastmod&gt;<br>    &lt;changefreq&gt;monthly&lt;/changefreq&gt;<br>    &lt;priority&gt;1.0&lt;/priority&gt;<br>  &lt;/url&gt;<br>&lt;/urlset&gt;</pre><p>In conclusion, I would suggest using these for fun hobby projects. However, you can also use them for the initial scaffolding of a serious app if you are willing to dig through code you didn’t write — and possibly don’t understand — and put the final touches.</p><p>The only other way to get beautiful apps deployed so quickly is templates and no/lo-code editors like <a href="https://webflow.com/">Webflow</a> or <a href="https://bootstrapstudio.io/">Bootstrap Studio</a>, which are all paid if you want decent results. In that sense, <a href="http://bolt.new">bolt</a> and <a href="http://lovable.dev">Lovable</a> are really recommendable. Try them out on your next hobby project.</p><p>You have nothing to lose.</p><h3>3. Trae — an AI-powered IDE</h3><p>I learned about <a href="http://trae.ai">Trae</a> from Theo, and I was amazed that <strong>TikTok</strong>, of all companies,<strong> </strong>made an IDE that could very well compete with Cursor, which raised $105M in their <a href="https://www.cursor.com/blog/series-b">Series B</a> in the beginning of this year (2025).</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FhqJDKTqCESE%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DhqJDKTqCESE&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FhqJDKTqCESE%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/5bbd9f0e0ac249931809975fc3a4c25f/href">https://medium.com/media/5bbd9f0e0ac249931809975fc3a4c25f/href</a></iframe><p>But when I tried to install Trae, it wasn’t available for Windows. Luckily, they had a waitlist signup form …</p><p>A few days ago, I finally got an email with a link to install Trae for Windows. I was excited and knew exactly what I wanted to build: <br>The chess cookies app.</p><p>So, I started building. First, I asked the AI for advice what frontend framework to use. I already knew I wanted to build a stand-alone app with <a href="https://www.rust-lang.org/">Rust</a> and <a href="https://v2.tauri.app/">Tauri</a> for optimal performance and easy front-end development. But I wasn’t sure about the frontend: vanilla HTML/CSS/JS, TS with <a href="https://preactjs.com/">preact</a>, Next with React or <a href="https://svelte.dev/">Svelte</a>/<a href="https://www.solidjs.com/">Solid</a>. I landed on Svelte because it’s simple, stable, compiled, and I enjoyed the <a href="https://svelte.dev/tutorial/svelte/welcome-to-svelte">tutorial</a> when I tried it a year or so ago. This is what I told Trae:</p><blockquote>Ok, I want to build a desktop app using Tauri with a Rust backend that can call custom Python code paired with a high performance instant feeling frontend (possibly even a SPA) using Svelte. Tell me how you would setup the development and production environments</blockquote><p>Because I was in builder mode, it immediately started going at it in the empty directory. Next time, I would definitely do the initial setup myself. Trae messed it up quite badly and installed incompatible (and outdated) packages: SvelteKit 1 with TypeScript 5, Tauri 1. In the end, I had to do most of the operational work, like following <a href="https://v1.tauri.app/v1/guides/getting-started/setup/sveltekit/">Tauri’s SvelteKit-Tutorial</a> and <a href="https://v2.tauri.app/start/migrate/from-tauri-1/">migration guide</a> myself.</p><p>But suddenly, everything just worked, and I had a running app with a text input and a submit button that allows the user to run arbitrary python code. Not exactly what I meant when I said “a Rust backend that can call custom Python code”, but good enough anyway.</p><figure><img alt="A screenshot of the running desktop app that shows an input field with the text “Hello, Medium” and an output field that contains the same text" src="https://cdn-images-1.medium.com/max/1024/1*rDcRJ1yWXXZJTJ1PemrucA.png" /></figure><p>Even though it was quite a hassle to get everything running, it felt quite magical. This is how the legendary <a href="https://karpathy.ai/">Andrej Karpathy</a> (student under <a href="https://www.cs.toronto.edu/~hinton/">Geoff Hinton</a>, founding member of OpenAI and head of Tesla AI) describes it:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/748/1*Jl1TYjuVfc_I5pA4HAV-mA.png" /></figure><blockquote>I just see stuff, say stuff, run stuff and copy paste stuff, and it mostly works.</blockquote><p>Is that the future of building software products? Maybe.</p><h3>Final Judgment</h3><p>The biggest issue with AI agents is probably their missing knowledge of anything recent (especially libraries) and their inability to produce non-text assets. I really advise you to check your dependencies or set them in the beginning when working with AI coding tools.</p><p>I’ll definitely spend the next few weeks using Trae as much as possible until it eventually becomes paid just like every other AI coding tool.</p><p>However, make no mistake. Not a single word in this article is AI-generated. If you like and respect my work or found any useful insights in this article, please leave a clap. Also, check out my other articles. There is probably something you will enjoy ; )</p><p>Signing Out,</p><p>Rashid Harvey</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=73d2494deea2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/design-bootcamp/using-agentic-ai-code-editors-73d2494deea2">Using Agentic AI Code Editors</a> was originally published in <a href="https://medium.com/design-bootcamp">Bootcamp</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Secret Santa (A Christmas Math Problem)]]></title>
            <link>https://medium.com/intuitionmath/a-christmas-math-problem-353ee68b6790?source=rss-fbc7a4c81650------2</link>
            <guid isPermaLink="false">https://medium.com/p/353ee68b6790</guid>
            <category><![CDATA[problems]]></category>
            <category><![CDATA[christmas]]></category>
            <category><![CDATA[simulation]]></category>
            <category><![CDATA[math]]></category>
            <category><![CDATA[mathematics]]></category>
            <dc:creator><![CDATA[R. Harvey]]></dc:creator>
            <pubDate>Sat, 14 Dec 2024 00:05:00 GMT</pubDate>
            <atom:updated>2025-01-21T02:19:29.349Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XUXEZjjzVw39tgK2k02z0Q.png" /><figcaption>— Dear Math, grow up and solve your own problems</figcaption></figure><h3>Christmas Math — Secret Santa</h3><p>Christmas is the perfect time to think about tricky math problems in the cozy embrace of your home, looking into the vast gray of the sky.</p><p>The particular problem presented in this article is inspired by a real Christmas tradition: <strong>Secret Santa</strong> (or “Wichteln” in Germany). Out of <em>n</em> people, every person gets assigned one other person whom they have to gift something for Christmas, so that everyone gets a gift. The question is: <strong>How likely is it that two people are assigned to another?</strong></p><p>Let’s do some examples:</p><ul><li>For 2 people, the answer is <strong>100%</strong>: They must be assigned to each other</li><li>For 3 people, it’s <strong>the exact opposite</strong>. If two people were assigned to each other, the third would be left out.</li><li>For 4 people, the question becomes a bit more difficult, but is still manageable with some basic knowledge of combinatorics: First, 1 picks 2. Now, 2 has a likelihood of <strong>1/3</strong> to pick 1, which results in two pairs, and a likelihood of 2/3 to pick 3 which results in a full cycle.</li></ul><p>Can we come up with a general formula for the probability of two people being assigned to each other? Does the probability decrease, increase, or diverge around a value when adding more people?</p><p>To find the probability <em>P</em>, we can divide <em>Aₚₐᵢᵣ</em>, the number of configurations that contain a pair, by <em>A</em>, the total number of all possible configuration.</p><h3>Simulation</h3><p>When solving math problems like this, writing some code to simulate the problem can be a good first approach to understand the problem better and avoid tedious manual work.</p><pre>from functools import cache<br>from itertools import chain<br><br>@cache<br>def _simulation(n1, n2):<br>    if not n1:<br>        return (tuple(),)<br>    return [<br>        *chain.from_iterable(<br>            [<br>                [(x1,x2),*result] for result in _simulation(<br>                    n1[1:], n2[:i]+n2[i+1:]<br>                )<br>            ] for i,x2 in enumerate((n2)) if (x1:=n1[0]) != x2<br>        )<br>    ]<br><br>def simulation(n):<br>    return _simulation(<br>        tuple(range(1,n+1)), tuple(range(1, n+1))<br>    )</pre><p>This simulation creates all possible gifting constellations for <em>n </em>people. @cache is a trade-off between memory and speed. The code runs faster, but requires more memory.</p><p>This was the result for <em>n=1</em> to <em>n=4</em>:</p><pre>[]<br>[[(1, 2), (2, 1)]]<br>[[(1, 2), (2, 3), (3, 1)], [(1, 3), (2, 1), (3, 2)]]<br>[[(1, 2), (2, 1), (3, 4), (4, 3)],<br> [(1, 2), (2, 3), (3, 4), (4, 1)],<br> [(1, 2), (2, 4), (3, 1), (4, 3)],<br> [(1, 3), (2, 1), (3, 4), (4, 2)],<br> [(1, 3), (2, 4), (3, 1), (4, 2)],<br> [(1, 3), (2, 4), (3, 2), (4, 1)],<br> [(1, 4), (2, 1), (3, 2), (4, 3)],<br> [(1, 4), (2, 3), (3, 1), (4, 2)],<br> [(1, 4), (2, 3), (3, 2), (4, 1)]]</pre><p>We can see that for n=4, there are 9 possibilities, 3 of which contain two pairs: the first, the fourth, and the ninth. This confirms our prediction from before. The probability is <strong><em>3/9 = 1/3</em></strong>.</p><p>For higher values of <em>n</em>, the list becomes exponentially larger, and so I only printed the totals <em>A</em> and the computation time:</p><pre>1       : 0 in 0.0 s<br>2       : 1 in 0.0 s<br>3       : 2 in 0.0 s<br>4       : 9 in 0.0 s<br>5       : 44 in 0.0 s<br>6       : 265 in 0.0 s<br>7       : 1854 in 0.0026404857635498047 s<br>8       : 14833 in 0.03130531311035156 s<br>9       : 133496 in 0.4279468059539795 s<br>10      : 1334961 in 5.012750625610352 s<br>11      : 14684570 in 80.85578060150146 s</pre><p>For <em>n=12</em> the computation time was in the tens of minutes until the program crashed with a MemoryError.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/626/1*73sBd7_a7grt93zLK6wexQ.png" /><figcaption>Reaching the limits of my RAM (&gt;19 GB; farthest right)</figcaption></figure><p>After a closer inspection of the results above, I noticed that the values for <em>A </em>were actually growing faster than any constant polynomial <em>nᵃ</em> or exponential <em>aⁿ. </em>Instead, they were growing almost like<em> n! ∈ O(nⁿ):</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/172/1*rBfPWw56MSWZ1ihUriDqYQ.png" /><figcaption>The series formula</figcaption></figure><p>This formula becomes most clear when looking at <em>n=10</em> where the value literally just got a <em>1</em> appended in the decimal system. But it also holds for all other values, as you can assure yourself.</p><p>For comparison, I plotted <em>nⁿ </em>(red), <em>n! </em>(blue), and the data points (green) from the simulation on a logarithmic scale.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*le3r2zTE84g-NJ0r1Tn02A.png" /><figcaption>A logarithmic plot on <a href="https://www.geogebra.org/classic/wuw826dy">geogebra</a></figcaption></figure><p>We see that the green data points align pretty closely to <em>n!</em>. For further analysis, it might make sense to approximate accordingly: <em>A = b</em>·<em>n!; b∈ </em>ℝ.</p><p>For example, if we approximate and write<em> aₙ </em>as this explicit form</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/137/1*oSOaujochDWpd0oOouB1wQ.png" /><figcaption>An approximate explicit series formula</figcaption></figure><p>and further assume <em>n </em>is even, we can split the odd and even terms with a surprising result.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/570/1*S8Yf4zGpthzr9T3gFQnQfQ.png" /><figcaption>The approximate explicit series for even n</figcaption></figure><p>Now let’s find <em>Aₚₐᵢᵣ </em>the number of constellations with a pair. Here I printed <em>Aₚₐᵢᵣ</em>/A<em>=P</em> for <em>n</em> up to 11.</p><pre>2       :  1 / 1 = 1.0<br>3       :  0 / 2 = 0.0<br>4       :  3 / 9 = 0.3333333333333333<br>5       :  20 / 44 = 0.45454545454545453<br>6       :  105 / 265 = 0.39622641509433965<br>7       :  714 / 1854 = 0.3851132686084142<br>8       :  5845 / 14833 = 0.39405379896177445<br>9       :  52632 / 133496 = 0.39425900401510156<br>10      :  525105 / 1334961 = 0.39334856973349785<br>11      :  5777090 / 14684570 = 0.39341226879643054</pre><p>Although, there is no pattern immediately visible for <em>Aₚₐᵢᵣ</em>, the resulting fraction is already starting to close in on <em>0.393…</em> at these fairly low values of <em>n</em>. Even without proof, we can already pretty confidently say that for higher values of<em> n </em>the probability will not change dramatically and stay somewhere around 39%. But let’s dive a bit deeper.</p><h3>A more thorough mathematical analysis</h3><p>The total combinations for a number n can be found by looking at all the combinations with one less person, and then inserting the nth person after every other person. This accounts for aₙ₋₁· (n-1) combinations.</p><pre>n=3:<br>for [(1, 2), (2, 1)]<br>  insert 3 after 2: [(1, 2), (2, 3), (3, 1)]<br>  insert 3 after 1: [(1, 3), (2, 1), (3, 2)]<br>-&gt; a_{n-1}*(n-1) = 1*2 = 2<br><br>n=4:<br>for [(1, 2), (2, 3), (3, 1)]<br>  1: [(1, 4), (2, 3), (3, 1), (4, 2)]<br>  2: [(1, 2), (2, 4), (3, 1), (4, 3)]<br>  3: [(1, 2), (2, 3), (3, 4), (4, 1)]<br>for [(1, 3), (2, 1), (3, 2)]<br>  1: [(1, 4), (2, 1), (3, 2), (4, 3)]<br>  2: [(1, 3), (2, 4), (3, 2), (4, 1)]<br>  3: [(1, 3), (2, 1), (3, 4), (4, 2)]<br>-&gt; a_{n-1}*(n-1) = 2*3 = 6</pre><p>But we are still missing some combinations where we introduce new pairs which are the combinations of aₙ₋₂ and n-1:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/318/1*4kS3DNxTBM6T6yRVNitkCA.png" /><figcaption>The missing part</figcaption></figure><pre>n=4:<br>for [(1, 2), (2, 1)]:<br>  1: [(1, 4), (2, 3), (3, 2), (4, 1)]<br>  2: [(1, 2), (2, 1), (3, 4), (4, 3)]<br>  3: [(1, 3), (2, 4), (3, 1), (4, 2)]<br>-&gt; a_{n-2}*(n-1) = 1*3 = 3</pre><p>If we put this together, we find the original formula, but also another formulation that reminds of the Fibonacci series (the Fibonacci series has an explicit formula, can we find one here as well?).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/527/1*V1Ub2UbS89zBiOjZQr0SEA.png" /><figcaption>A strange identity of series</figcaption></figure><p>Now for the tricky part: To solve this problem, let’s consider any constellation <em>C</em> of <em>n</em> people to consist of <em>k</em> sets with a might of at least <em>2</em>. More generally: “with a might of at least m.” We can redefine <em>P</em> in this way:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/565/1*briqVIiKriHb784UxE-EVw.png" /><figcaption>Formal definitions of constellations and their amount</figcaption></figure><p>Unfortunately, I was not able to further break this down purely mathematically. Do you have an idea?</p><p>However, using <a href="https://stackoverflow.com/a/71233372">this code from Stack Overflow</a> to generate the permutations, I made a little script that comes farther than the first one, but still not nearly as far as I would like. This is because generating all possible partitions is quite tedious work (n!), and Python is not the best to do this kind of work.</p><pre>def get_prob(n):<br>    lst = list(range(1, n+1))<br>    a2 = [*series(n)][-1] # the series we defined earlier<br>    s3 = get_partitions(lst, 3)<br>    a3 = sum(<br>        math.prod(<br>            fact(len(c)-1) for c in part<br>        ) for part in s3<br>    )<br>    print(n, &quot;:&quot;, a2-a3, &quot;/&quot;, a2, &quot;-&gt;&quot;, (a2-a3)/a2)</pre><p>Here is the result for <em>n</em> up to 16</p><pre>3 : 0 / 2 -&gt; 0.0<br>4 : 3 / 9 -&gt; 0.3333333333333333<br>5 : 20 / 44 -&gt; 0.45454545454545453<br>6 : 105 / 265 -&gt; 0.39622641509433965<br>7 : 714 / 1854 -&gt; 0.3851132686084142<br>8 : 5845 / 14833 -&gt; 0.39405379896177445<br>9 : 52632 / 133496 -&gt; 0.39425900401510156<br>10 : 525105 / 1334961 -&gt; 0.39334856973349785<br>11 : 5777090 / 14684570 -&gt; 0.39341226879643054<br>12 : 71555121 / 176214841 -&gt; 0.40606750597130464<br>13 : 901364100 / 2290792932 -&gt; 0.3934725340771219<br>14 : 12618959353 / 32071101049 -&gt; 0.3934682296600936<br>15 : 189284859750 / 481066515734 -&gt; 0.393469205523884<br>16 : 3069424181265 / 7697064251745 -&gt; 0.39877855775585236</pre><p>The absolute numbers are actually getting ridiculous. Further analysis would have to reduce the problem and find some kind of formula for A₃, like the one we found for A₂.</p><p>So to answer the question: When doing <strong>Secret Santa</strong>, for a medium-sized group (6–16), the chance that at least two people will prepare a gift for each other is approximately <strong><em>2/5</em></strong>. Most likely, this is also true for larger groups.</p><p>I hope you had some fun and could follow. If not, feel free to ask for clarification. If yes, leave some feedback. Did you find a better or a different solution? Do you know of more resources regarding the topic? Share your thoughts.</p><p>The LaTeX renders were made using <a href="https://www.quicklatex.com/">quicklatex.com</a>. Give it a try!</p><p>Signing out,</p><p>Rashid Harvey</p><p>PS: Find out <a href="https://medium.com/intuitionmath/a-more-intuitive-way-of-comparing-sets-ffc98c0fa06a">a better way for comparing sets,</a> or <a href="https://medium.com/intuitionmath/why-all-numbers-are-imaginary-3b88eda407e6">why all numbers are imaginary</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=353ee68b6790" width="1" height="1" alt=""><hr><p><a href="https://medium.com/intuitionmath/a-christmas-math-problem-353ee68b6790">Secret Santa (A Christmas Math Problem)</a> was originally published in <a href="https://medium.com/intuitionmath">IntuitionMath</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[7 Rust crates you will probably need]]></title>
            <link>https://medium.com/@r.harvey/7-rust-crates-you-will-probably-need-4def87a763c8?source=rss-fbc7a4c81650------2</link>
            <guid isPermaLink="false">https://medium.com/p/4def87a763c8</guid>
            <category><![CDATA[tutorial]]></category>
            <category><![CDATA[recommendations]]></category>
            <category><![CDATA[package]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[R. Harvey]]></dc:creator>
            <pubDate>Tue, 16 Apr 2024 13:35:15 GMT</pubDate>
            <atom:updated>2024-04-16T13:51:20.149Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JT8FyZITaU7ZqipciETMRA.jpeg" /><figcaption>Cargo</figcaption></figure><p>Rust is a programming language with a very minimal standard (std) library. This is by design to make the programming language as light-weight as possible. However, this also means that you will need to install external libraries (called crates) for most projects. But at least it is very simple to install crates because Rust, in contrast to C for example, has a dedicated crate manager (cargo) that does most of the work for you. Another cool thing is that the Rust ecosystem is very focused on providing <strong>one </strong>good crate for each use case. In most ecosystems, you will see that there are several competing packages that essentially do the same thing/solve the same problem (JavaScript is famous for this). In Rust, however, there is mostly <strong>exactly one</strong> crate that you are looking for and that all sources will point you towards. Which is very relieving as you never have to make decisions on which crates to use, when it probably doesn’t make a difference anyway. Also, this makes it so that these crates essentially become an extended standard library.</p><p>In this article, I want to show you a few of those crates and introduce them shortly, which you will likely need to build your next Rust project. I also often fell into the trap of trying to reinvent the wheel. Maybe I can save you from making the same mistakes with this comprehensive article.</p><h3>Random</h3><p>Almost every language in the world has support for rudimentary random number generation (RNG) built-in. Rust is an exception. To generate (pseudo)random numbers, you should use the <a href="https://docs.rs/rand/latest/rand/"><em>rand</em></a> crate. To add it to your project, you can simply run cargo add rand in your command line. This is the same for all the crates I am going to show you here.</p><p>You can then use it to generate numbers, distributions, and even cryptographically secure random values. For example, to generate a random number from 1 to 10 as a CLI application, you can do <a href="https://rust-lang-nursery.github.io/rust-cookbook/algorithms/randomness.html#generate-random-numbers-within-a-range">this</a>:</p><pre>use rand::Rng;<br><br>fn main() {<br>    let mut range = rand::thread_rng();<br>    println!(&quot;{}&quot;, range.gen_range(1..=10));<br>}</pre><h3>Lazy statics</h3><p>In Rust, there are four kinds of variables. There are compile-time constants: const, run-time constants: static, immutable values (default): let, and finally there are mutable values: let mut. Sometimes you want to have static variables that are only initialized at run-time. This is however not possible by default, because even though static variables are only run-time constants, they must be known at compile-time.</p><p>However, when dealing with the operating system (opening files, starting threads or opening network ports, etc.) for example, it is necessary to create these static variables during the run-time. Ideally at the start of the program are at least before they are used the first time. These lazy statics are virtually staticvariables that are automatically initialized when they are accessed the very first time:</p><blockquote><a href="https://crates.io/crates/lazy_static">lazy_static</a> is one of the foundational crates in the Rust ecosystem.<br> It lets us use static variables without an explicit initialization call.</blockquote><blockquote>— Max on <a href="https://dev.to/rimutaka/rusts-lazystatic-usage-benchmarks-and-code-deep-dive-1bic">dev.to</a></blockquote><h3>Regex</h3><p>Let’s see an example use case: Compiling a regular expression (regex). Theoretically, a regex should be compiled at compile-time, duh! However, afaik there is currently no stable implementation. Instead, the regex needs to be compiled during run-time. To still use the benefits of static variables, like global access from anywhere, we can use <a href="https://crates.io/crates/lazy_static">lazy_static</a> in combination with the <a href="https://crates.io/crates/regex">regex </a>crate. If you don’t know how regular expressions work or want to learn more, <a href="http://regexr.com">regexr.com</a> is a good place to go. In this example, we use a regular expression for YYYY-MM-DD dates.</p><p>With lazy_static! we can set up our lazy statics. Regex::new() compiles a given string into a regular expression. Finally, we can use our expression to match a given date object.</p><pre>use lazy_static::lazy_static;<br>use regex::Regex;<br><br>lazy_static! {<br>    /// This is the static regex<br>    static ref EXPR: Regex = Regex::new(r&quot;^\d{4}-\d{2}-\d{2}$&quot;).unwrap();<br>}<br><br>fn main() {<br>    let date = &quot;2021-01-01&quot;;<br>    if EXPR.is_match(date) {<br>        println!(&quot;{} is a valid date&quot;, date);<br>    } else {<br>        println!(&quot;{} is not a valid date&quot;, date);<br>    }<br>}</pre><h3>Error handling</h3><p>In Rust, errors are return values and there exists simple syntax to propagate errors.</p><p>Here is an example. Error1 and Error2 are custom errors. Our functions raise_error1() and raise_error2() return these errors with a Result return type. It either contains the actual value or the error type. In combine_errors() we propagate both errors using the ?. This will return any error in the outer scope immediately, or pass on the result value if there is no error.</p><pre><br>struct Error1;<br>struct Error2;<br><br>fn raise_error1() -&gt; Result&lt;(), Error1&gt; {<br>    Ok(())<br>}<br><br>fn raise_error2() -&gt; Result&lt;(), Error2&gt; {<br>    Ok(())<br>}<br><br>fn combine_errors() {<br>    raise_error1()?;<br>    raise_error2()?;<br>    Ok(())<br>}</pre><p>You might have noticed that I didn’t add a type for combine_errors() because it is ambiguous. Instead, we would need to use a dynamic Box as shown in <a href="https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/boxing_errors.html">the Rust-by-example book</a>. However, an even easier approach is to use the rust crate <a href="https://docs.rs/anyhow/latest/anyhow/">anyhow</a>. Install anyhow by running cargo add anyhow in your CLI. You do need to implement std::fmt::Display and std::error::Error to use it, but that is pretty straightforward and not necessary when you don’t use custom errors.</p><pre><br>#[derive(Debug)]<br>struct Error1;<br>#[derive(Debug)]<br>struct Error2;<br><br>impl std::fmt::Display for Error1 {<br>    fn fmt(&amp;self, f: &amp;mut std::fmt::Formatter) -&gt; std::fmt::Result {<br>        write!(f, &quot;Error1&quot;)<br>    }<br>}<br><br>impl std::fmt::Display for Error2 {<br>    fn fmt(&amp;self, f: &amp;mut std::fmt::Formatter) -&gt; std::fmt::Result {<br>        write!(f, &quot;Error2&quot;)<br>    }<br>}<br><br>impl std::error::Error for Error1 {}<br>impl std::error::Error for Error2 {}<br><br>fn raise_error1() -&gt; Result&lt;(), Error1&gt; {<br>    Err(Error1)<br>}<br><br>fn raise_error2() -&gt; Result&lt;(), Error2&gt; {<br>    Err(Error2)<br>}<br><br>fn combine_errors() -&gt; anyhow::Result&lt;()&gt; {<br>    raise_error1()?;<br>    raise_error2()?;<br>    Ok(())<br>}</pre><p>I would also love to refer you to <a href="https://antoinerr.github.io/blog-website/2023/01/28/rust-anyhow.html#creating-an-error">this blog post</a> by AntoineRR where he introduces the topic just like me, but then also goes into more detail into how you can create your own anyhow errors.</p><h3>Parsing</h3><p>Parsing has always been an unreasonably difficult task in statically-typed languages. However, there is one ubiquitous module that makes parsing in Rust easy-peasy: Serde. I will let it speak for itself:</p><blockquote>Serde is a framework for <strong><em>ser</em></strong>ializing and <strong><em>de</em></strong>serializing Rust data structures efficiently and generically.</blockquote><blockquote>The Serde ecosystem consists of data structures that know how to serialize and deserialize themselves along with data formats that know how to serialize and deserialize other things. Serde provides the layer by which these two groups interact with each other, allowing any supported data structure to be serialized and deserialized using any supported data format.</blockquote><blockquote>— Serde documentation</blockquote><p>However, most of the time, you will use a derived package that can serialize and deserialize a specific data format, like the very popular JSON. The corresponding crate is serde_json. Install it with, you guessed it, cargo add serde_json. But we will also need serde: cargo add serde --features serde_derive. With everything set up, let’s implement a simple program.</p><p>The JSON file looks like this</p><pre>{<br>  &quot;value&quot;: 0<br>}</pre><p>The program tries to read this file. If it doesn’t exist, the program will assume a default. Then we increment the value and write the JSON back to the file. If the JSON doesn’t fit our requirements, we can simply raise an error using anyhow.</p><pre>use anyhow::{Result, anyhow};<br>use serde_json::{from_str, to_string, Value};<br><br>fn main() -&gt; Result&lt;()&gt; {<br>    const FILE: &amp;str = &quot;data.json&quot;;<br>    let contents = std::fs::read_to_string(FILE)<br>        .unwrap_or(&quot;{\&quot;value\&quot;: 0}&quot;.to_string());<br><br>    let mut data: Value = from_str(&amp;contents)?;<br><br>    let error = Err(anyhow!(&quot;Unexpected json&quot;)); <br><br>    match data {<br>        Value::Object(ref mut map) =&gt; {<br>            if let Some(Value::Number(value)) = map.get(&quot;value&quot;) {<br>                if let Some(number) = value.as_i64() {<br>                    map.insert(&quot;value&quot;.into(), (number + 1).into());<br>                    std::fs::write(<br>                        FILE,<br>                        to_string(&amp;data).unwrap(),<br>                    )?;<br>                    return Ok(());<br>                }<br>            }<br>            return error<br>        }<br>        _ =&gt; error,<br>    }<br>}</pre><p>anyhow makes this so much easier by allowing us to freely return different types of errors, and even custom errors. This code works perfectly fine and increments the value reliably and extremely fast (&lt;20ms).</p><pre>{&quot;value&quot;:7};</pre><p>When I give it an incorrectly formatted file like the above, for example, it gives me the error:</p><pre>Error: trailing characters at line 1 column 12</pre><p>When there is a different error, it returns the Error: Unexpected JSON we specified above. We could make this error message even more informative, but this isn’t necessary for now.</p><p>However, you might think this is quite a lot of code just to increment a value in JSON, and you are right. There is a much better, more declarative, way. This is Rust, a statically-typed language with extremely powerful macros, that we will make use of by defining a strict statically-typed interface which makes both the code and the errors much more readable:</p><pre>use anyhow::Result;<br>use serde::{Deserialize, Serialize};<br>use serde_json::{from_str, to_string};<br><br>#[derive(Serialize, Deserialize)]<br>struct JSON {<br>    value: i32<br>}<br><br>fn main() -&gt; Result&lt;()&gt; {<br>    const FILE: &amp;str = &quot;data.json&quot;;<br>    let contents = std::fs::read_to_string(FILE)<br>        .unwrap_or(&quot;{\&quot;value\&quot;: 0}&quot;.to_string());<br><br>    let mut json: JSON = from_str(&amp;contents)?;<br><br>    json.value += 1;<br><br>    std::fs::write(FILE, to_string(&amp;json)?)?;<br><br>    Ok(())<br>}</pre><p>We use the JSON struct to tell serde how we expect our data to look like. Then serde_json has an easy time to convert the JSON, so that we can increment the value field and then convert it back. The errors are also much more helpful:</p><blockquote>{“valu”:24}</blockquote><blockquote>Error: missing field ‘value’ at line 1 column 11</blockquote><blockquote>{&quot;value&quot;:&quot;&quot;}</blockquote><blockquote>Error: invalid type: string “”, expected i32 at line 1 column 11</blockquote><p>So you have seen how serde, serde_json and anyhow can make your life parsing JSON much easier by providing concise, declarative APIs and simple error handling.</p><h3>Async</h3><p>If you want an introduction to async in Rust, I would recommend the free and open-source <a href="https://rust-lang.github.io/async-book/">Asynchronous Programming in Rust</a> book.</p><p>Even though Rust supports syntax for async, it does not provide anything to actually run asynchronous code <strong>at all</strong>. The real work is left to constantly evolving community crates, like <a href="https://crates.io/crates/tokio">tokio</a>. Tokio is <strong>the crate to use</strong> when you need an asynchronous run-time in Rust. For this example, you should install it with all features enabled: cargo add tokio --features=full</p><h3>HTTP client</h3><p>This is where most other standard libraries stop as well, and here we can also observe many similar library implementations. However, a good starting point is <a href="https://github.com/seanmonstar/reqwest">reqwest</a>. Which is a feature-full library that supports asynchronous and synchronous HTTP fetching. In our example, we will try to request two different resources simultaneously. To do that, we use a join, which is provided by tokio. It also enables us to have an async main function, which is very handy.</p><pre>#[tokio::main]<br>async fn main() {<br>    let fut1 = reqwest::get(&quot;https://www.rust-lang.org&quot;);<br>    let fut2 = reqwest::get(&quot;https://tokio.rs/tokio/tutorial/hello-tokio&quot;);<br>    match tokio::join!(fut1, fut2) {<br>        (Ok(resp1), Ok(resp2)) =&gt; {<br>            println!(&quot;Rust lang: {:#?}&quot;, resp1.status());<br>            println!(&quot;Tokio: {:#?}&quot;, resp2.status());<br>        }<br>        _ =&gt; println!(&quot;There was an error&quot;)<br>    }<br>}</pre><p>This will get <a href="https://www.rust-lang.org">https://www.rust-lang.org</a> and <a href="https://tokio.rs/tokio/tutorial/hello-tokio">https://tokio.rs/tokio/tutorial/hello-tokio</a> at the same time and tell us whether the requests were successful. The result should be:</p><pre>Rust lang: 200<br>Tokio: 200</pre><p>I hope this short article helped you. If you have any further questions or remarks, please leave a comment or visit the package documentation.</p><p>So the 7 Rust crates you will probably need in Rust are:</p><ol><li>Random</li><li>Lazy static</li><li>Regex</li><li>Anyhow</li><li>Serde</li><li>Tokio</li><li>Reqwest (or a different HTTP client)</li></ol><p>If you have any must-have crates I forgot, add them in the comments. Also, if you have any questions, don’t hesitate to ask. All the code can also be found <a href="https://github.com/theRealProHacker/rust-crates">on GitHub</a> with quick-install instructions.</p><p>Signing out,</p><p>Rashid Harvey</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4def87a763c8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why inheritance is still awesome]]></title>
            <link>https://medium.com/@r.harvey/why-inheritance-is-still-awesome-36a0765d4757?source=rss-fbc7a4c81650------2</link>
            <guid isPermaLink="false">https://medium.com/p/36a0765d4757</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[inheritance]]></category>
            <category><![CDATA[programming-paradigms]]></category>
            <dc:creator><![CDATA[R. Harvey]]></dc:creator>
            <pubDate>Fri, 03 Nov 2023 00:22:18 GMT</pubDate>
            <atom:updated>2023-11-03T00:22:18.910Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FKZBePEH_9cVJpToS7H3Bg.png" /><figcaption>Inheritance over Composition</figcaption></figure><p>Inheritance got a lot of hits recently. Many say that interfaces, or protocols as they are called in Python, are just better: Composition over inheritance, they say. In this article, I want to show you how you can write enjoyable code using inheritance that triumphs over composition.</p><p>As an example, let’s start with code that expects any object that can be drawn. That means it has a draw method that expects a surface and a rectangle and draws something to the surface in the given bounds. This seems like a perfect job for a protocol. In the following code samples, I am making up some types and methods and assuming they are provided. I am borrowing mostly from pygame, as I have used it a lot, especially most recently.</p><pre>from typing import Protocol<br><br>class Drawable(Protocol):<br>  def draw(self, surface: Surface, rect: Rect):<br>    ...<br><br>def my_code(drawable: Drawable):<br>  &quot;&quot;&quot; Do something cool here &quot;&quot;&quot;</pre><p>And our job is done. This looks perfect! Now, any static type checker will ring alarm bells if anyone even tries to pass something that is not a drawable. But, anything that is drawable works fine. We could for example make a drawable color.</p><pre>class DrawableColor:<br>  def __init__(self, color: Color):<br>    self.color = color<br><br>  def draw(self, surface: Surface, rect: Rect):<br>    draw_rect(surface, self.color, rect)<br><br>my_code(DrawableColor(&quot;red&quot;))</pre><h3>If this is so awesome, why use inheritance?</h3><p>Well, I’ll show you. Instead of using protocols, we will use an inheritable object as a type.</p><pre>class Drawable:<br>  def draw(self, surface: Surface, rect: Rect):<br>    ...<br><br>def my_code(drawable: Drawable):<br>  &quot;&quot;&quot; Do something equally cool here &quot;&quot;&quot;</pre><p>Now, you might ask yourself why this is better. Don’t I just need to add more code to every drawable class?</p><pre>from _ import Drawable<br><br>class DrawableColor(Drawable):<br>  def __init__(self, color: Color):<br>    self.color = color<br><br>  def draw(self, surface: Surface, rect: Rect):<br>    draw_rect(surface, self.color, rect)<br><br>my_code(DrawableColor(&quot;red&quot;))</pre><p>Well yes, but the upsides are only coming</p><h3>Supplying extending methods</h3><p>Due to our careful construction, we can now easily add extending methods to all Drawables by simply adding that method to the Drawable class. For this example, we might want to add a method for creating a new surface from the drawable given a size.</p><pre>class Drawable:<br>  def draw(self, surface: Surface, rect: Rect):<br>    ...<br><br>  def to_surface(self, size: tuple[int, int])-&gt;Surface:<br>    surf = Surface(size)<br>    self.draw(surf, Rect((0,0), size))<br>    return surf</pre><p>This code now gives every Drawable anywhere this method. But they can also override the method if the default implementation is not desirable!</p><p>Of course, there could be collisions theoretically, if some drawable already implemented its own to_surface method for a different use before adding the extending behaviour. So theoretically it’s a breaking change. But in most cases, it’s unlikely. Also, I am assuming you are developing small libraries quickly and have an overview over most inheritors. If you are using <a href="https://semver.org/">SemVer</a> and Drawable is part of your public API, you should theoretically bump your major version nonetheless when adding such a functionality after 0.y.z.</p><h3>A new concept: Creating a default base object</h3><p>Well, instead of just extending the functionality of our class, we can also supply a default implementation in our base class. This has several advantages:</p><ol><li>When you inherit the base class, everything is already set up. You can then implement every method piece by piece</li><li>When implementing the methods, you can look into the default implementation for reference on what a method is supposed to do, additionally to the documentation.</li></ol><p>To elaborate, let’s assume your protocol/base class has n methods that must be implemented. Using a protocol, every single method needs to be implemented correctly before you can run the code or make a commit, etc. This is very limiting, especially when n ⇒ ∞ or if you have no clue about a good implementation for certain functions that perhaps you know will never actually be called in your specific circumstances.</p><p>I want to give you a last example and then wrap this up: a Stream that you can send data to.</p><pre>from typing import Self<br><br>class Stream:<br>  def send(self, item)-&gt;Self:<br>    print(item)<br>    return Self<br><br>def my_code(stream: Stream):<br>  stream.send(&quot;Hello, &quot;)<br>  stream.send(&quot;World!&quot;)</pre><p>Now you can build your own Stream piece by piece. You don’t need to implement all the methods yourself (in this example it’s only 1 thankfully) or any method at all:</p><pre>class MyStream(Stream):<br>  ...<br><br>my_code(MyStream())</pre><p>The above works just as well as the fully implemented version (which would have been required to work with a Protocol):</p><pre>class MyStream(Stream):<br>  def __init__(self, file: Pyth):<br>    self.file = open(file, &quot;a&quot;)<br><br>  # Here we see another advantage, <br>  # you can leave out the explicit type annotations<br>  def send(self, item):<br>    self.file.write(str(item))<br>    return self<br>  <br>  def __del__(self):<br>    # I know this isn&#39;t the most beautiful code<br>    self.file.close()<br><br>my_code(MyStream(&quot;out.txt&quot;))</pre><p>The only issue with this approach is really only the __init__ function. If your default implementation needs an __init__ function with parameters to work, then that could be a problem because your inheritors are then not totally free to do their own thing. So, you should refrain from that by either writing a custom init function (not a method) or hard-coding any parameters.</p><p>In this article, you have learned why inheritance can often be better than protocols when you focus on implementing things rapidly.</p><ol><li>The difference in code is minimal</li><li>You can easily extend the functionality of your objects in one place with minimal risk and the possibility to override the default</li><li>You can treat the base class as a reference implementation, which allows you to code incrementally.</li><li>Quality of life changes: Less explicit typing and more auto-completion</li></ol><figure><img alt="VS Code autocompletes the send method when implementing it in the subclass" src="https://cdn-images-1.medium.com/max/1024/1*F8Oc4qSJgtCUYQlj7F9zNQ.gif" /><figcaption>Auto-completion</figcaption></figure><p>I hope this made sense to you, and thanks a lot for reading so far. Please tell me what you think about the examples given or the topic inheritance in general. Also, if you learned something new, clap for the article. My mission is to inspire my readers to discover new ideas.</p><p>Signing Out,</p><p>Rashid Harvey</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=36a0765d4757" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How can you throw a ball as far as possible?]]></title>
            <link>https://medium.com/@r.harvey/how-can-you-throw-a-ball-as-far-as-possible-e154533fdd81?source=rss-fbc7a4c81650------2</link>
            <guid isPermaLink="false">https://medium.com/p/e154533fdd81</guid>
            <category><![CDATA[analysis]]></category>
            <category><![CDATA[mechanical-engineering]]></category>
            <category><![CDATA[algebra]]></category>
            <category><![CDATA[learning]]></category>
            <category><![CDATA[physics]]></category>
            <dc:creator><![CDATA[R. Harvey]]></dc:creator>
            <pubDate>Sat, 23 Sep 2023 17:48:03 GMT</pubDate>
            <atom:updated>2023-09-25T08:19:59.014Z</atom:updated>
            <content:encoded><![CDATA[<p>This is one of the most popular examples when introducing classical mechanics: “In which angle should an athlete throw a ball to throw the farthest,” and if you ever studied the question, you probably remember that it was 45°. Which is almost correct. In this article, we will learn the real answer. But first, we should understand where the 45° come from. If you already grasped this fully, skip to the next section.</p><h3>The simplest case</h3><p>The simplest case is where the ball is thrown from the ground and lands on the same even ground. Then there are two important equations we need to find the optimal angle. The first gives us the x-position of our ball as a function of time. The second gives us the y-position also as a function of time:</p><blockquote><em>I</em> x <em>=</em> vx<em>*</em>t</blockquote><blockquote><em>II</em> y = vy*t<em> - ½</em>gt<em>²</em></blockquote><p>Where <em>vx </em>and <em>vy </em>are the horizontal and vertical starting velocities of the ball, <em>t </em>is the time and <em>g </em>is the earths constant of gravitation which is generally set to 9.81 m/s² though it depends on the location on earth. We assume that the starting velocity of our ball is independent of the angle. So we are ignoring all anatomical factors. At least for me, those are big mysteries anyway.</p><p>We want to know how far the ball has travelled when it touches the ground. Therefore, we set <em>y</em> to 0, and additionally we will replace <em>vy</em> and <em>vx </em>with the starting velocity <em>v</em> and the starting angle <em>a</em> using simple trigonometry:</p><figure><img alt="An image showing the trigonometric relationship between v, vy and vx" src="https://cdn-images-1.medium.com/max/323/1*2lUtwWoZHT-GLuWnEzIjGQ.png" /></figure><blockquote>I <em>vx</em> = v<em> * cos </em>a</blockquote><blockquote>II vy = v<em> * sin </em>a</blockquote><p>0 = <em>v </em>sin <em>a </em>* <em>t — ½</em>g<em>t² </em>generally has two solutions: <em>t </em>= 0 and <em>t </em>= 2<em>v </em>sin <em>a </em>/ <em>g. </em>However, the first solution is not interesting to us because this is at the exact moment the ball leaves the ground. Instead, we need the second solution when the ball hits the ground after flying through the air.</p><blockquote><em>For simplicity, I will replace</em> sin a<em> with</em> s <em>and </em>cos a <em>with </em>c<em>.</em></blockquote><p>When we substitute <em>t</em> into I<em>, </em>we get <em>x = </em>2<em>v²s</em>c/<em>g </em>(or alternatively, <em>x</em> = <em>v</em>²sin(2<em>a</em>)/<em>g</em>)<em>. </em>To get the angle that maximizes <em>x</em> we need to find the angle where <em>x’</em> = 0. To get to the exciting part, I will skip the rest and link <a href="https://qr.ae/pKRCTl">this answer</a> on Quora.</p><h3>Throwing the ball a bit better</h3><p>Now we want to step up our game and throw a bit farther. For example, we could maybe start throwing from above the ground? But for that, we also need to step up our physics game a bit. To begin with, we need to add a starting height <em>h </em>to our second equation.</p><blockquote><em>II</em> y = h+vy*t<em> — ½</em>gt<em>²</em></blockquote><p>Again, we set <em>y</em> to 0. Afterward, we move all t terms to one side and then complete the square to solve for t:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/854/1*ozTLCnTezdh4P9BHT5ieRg.png" /></figure><p>Now we use the binomial, take the square root and move over vy/g</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/854/1*HN9izDJ_enZNhA1-ybrJbg.png" /></figure><p>Now we put that into I:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/854/1*bCOu5l1MmNXan1kvYlkSJQ.png" /></figure><p>And there we have a formula for <em>x</em>. We can now plot this given some <em>g</em>, <em>h</em> and <em>v:</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DbfyHBz2s_6D58u6h8cJXQ.png" /><figcaption>Plot for x in radians with v = 5, g = 9.81 and <em>h</em> = 2</figcaption></figure><p>We immediately notice some things. The curve isn’t really symmetrical anymore. Before, the distance at 30° was the same distance as at 60°, for example. Now, the curve is skewed towards smaller degrees. And while you can’t read the maximum exactly, it looks like it’s less than 45°=π/4. To find the maximum exactly, we equal the differential to 0 again: <em>x’</em> = 0.</p><p>To start, we pull out <em>v/g </em>right at the beginning and apply all the differential rules we learned in analysis:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/805/1*tde9rjX_a7WWP3RdPvIF3w.png" /></figure><p>Now we multiply with the root term to isolate the root to one addend, then we move that to the other side and square the whole equation. On the way, we also replace cos(<em>a</em>)² with 1-sin(<em>a</em>)² and sin(<em>a</em>)² with r:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/801/1*XzZoaaUxkXfdi_AiTuQD_A.png" /></figure><p>This looks quite complicated, but bear with me because all of this collapses piece by piece to:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/120/1*TcSySROJXyrPWLx0IDQR4Q.png" /></figure><p>Resubstitute:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/212/1*IMOOw281IHwh5zH0tTHKAw.png" /></figure><p>Just for fun, let’s try <em>h </em>= 0:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/327/1*o01NjMaqgJ6tQV4dc_SJig.png" /></figure><p>That worked really well 🥳. We didn’t just find the general solution, we also witnessed extremely complicated and long equations and terms (I spared you from most of it) simplifying into such a concise formula, which feels quite awesome.</p><p>Thanks for staying with me on this adventurous ride, and maybe we’ll learn about the effect of air resistance in the next article.</p><p>Please leave me a comment on how you found the article, what you understood, what you didn’t and what I could improve.</p><p>Signing Out,</p><p>Rashid Harvey</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e154533fdd81" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to detect whether a pseudo-element was clicked in JS]]></title>
            <link>https://medium.com/@r.harvey/how-to-detect-whether-a-pseudo-element-was-clicked-in-js-862deca19cca?source=rss-fbc7a4c81650------2</link>
            <guid isPermaLink="false">https://medium.com/p/862deca19cca</guid>
            <category><![CDATA[problem-solving]]></category>
            <category><![CDATA[stackoverflow]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[html]]></category>
            <category><![CDATA[pseudo-element]]></category>
            <dc:creator><![CDATA[R. Harvey]]></dc:creator>
            <pubDate>Mon, 18 Sep 2023 21:39:11 GMT</pubDate>
            <atom:updated>2023-09-19T14:19:33.339Z</atom:updated>
            <content:encoded><![CDATA[<p>Today I wanted to build something very simple. An element that can be dismissed by a small button that pops up on hover. However, the button shouldn’t be in the HTML/DOM, so I used an ::after pseudo-element for the button instead. This part so far was pretty easy.</p><p>I’ve prepared an example in CodePen. It’s very simple code that lets an ::after element with an x appear in a .dismissable when it&#39;s :hovered.</p><p><a href="https://codepen.io/theRealProHacker/embed/zYyEXaG?default-tab=html%2Cresult&amp;editable=true">CodePen Embed - Clickable pseudo-element</a></p><p>Now the hard part is making it clickable. You can’t really select the pseudo-element, and it also doesn’t appear in the click event when listening to a click event on the .dismissable.</p><p>Naturally, I turned to the internet and stumbled onto exactly the question I had: “<a href="https://stackoverflow.com/questions/67404456/jquery-detect-after-css-selector-click-event">jquery detect ::after css selector click event</a>.” This question is a prime example of how bad SO actually is, and the situation depresses me a lot, to the point that I don’t ask any questions anymore. The question was closed as a duplicate, even though the referenced question is absolutely different.</p><figure><img alt="No one on StackOverflow could answer this question" src="https://cdn-images-1.medium.com/max/500/1*O9KdVwi1C2MGEcZ2ROTUvA.png" /></figure><p>Neither the original question nor the referenced question had any answers that were able to solve my problem. I still liked both the original question and the single answer, as they could be valuable to others, and I wanted to show those people some love for their time and effort.</p><h3>Detecting that the pseudo-element was clicked</h3><p>So now let&#39;s get to the real stuff. The essential idea is a mixture of two ideas.</p><ol><li>Get the attributes of the pseudo-element with getComputedStyle</li><li>Check the mouse position relative to the pseudo-element</li></ol><p>We can get the pseudo-elements style with getComputedStyle(elem, &quot;:after&quot;) this is extremely useful in our situation.</p><p>With .getPropertyValue we can then get the computed values of top, height, left and width, which allow us to delimit the pseudo-element. Importantly, the computed values are just strings that look like {num}px. To get just the number, we slice off the last two characters and then parse the strings as Numbers.</p><p>We get the mouse positions relative to the .dismissable by accessing the layerX and layerY attributes.</p><p>The final step is a simple bounds check. If it is successful, we can do whatever we desire. In this case, I made the .dismissable simply disappear by setting its display attribute to &quot;none&quot;, but normally you might want to do different things as well.</p><pre>const handler = (e)=&gt;{<br>  // First we get the pseudo-elements style<br>  const target = e.currentTarget || e.target<br>  const after = getComputedStyle(target, &quot;:after&quot;)<br>  if (after) {<br>    // Then we parse out the dimensions<br>    const atop = Number(after.getPropertyValue(&quot;top&quot;).slice(0, -2))<br>    const aheight = Number(after.getPropertyValue(&quot;height&quot;).slice(0, -2))<br>    const aleft = Number(after.getPropertyValue(&quot;left&quot;).slice(0, -2))<br>    const awidth = Number(after.getPropertyValue(&quot;width&quot;).slice(0, -2))<br>    // And get the mouse position<br>    const ex = e.layerX<br>    const ey = e.layerY<br>    // Finally we do a bounds check (Is the mouse inside of the after element)<br>    if (ex &gt; aleft &amp;&amp; ex &lt; aleft+awidth &amp;&amp; ey &gt; atop &amp;&amp; ey &lt; atop+aheight) {<br>      console.log(&quot;Button clicked&quot;)<br>      target.style.display = &quot;none&quot;<br>    }<br>  }<br>}</pre><p>Again, try this out in the CodePen and have fun:</p><p><a href="https://codepen.io/theRealProHacker/embed/zYyEXaG?default-tab=js%2Cresult&amp;editable=true">CodePen Embed - Clickable pseudo-element</a></p><p>Signing Out,</p><p>Rashid Harvey</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=862deca19cca" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>