<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Pat’s Tech Weblog</title><link>https://patstechweblog.com</link><atom:link href="https://patstechweblog.com/feed.xml" rel="self" type="application/rss+xml"/><description>Makin’ stuff</description><language>en-us</language><pubDate>Wed, 13 Nov 2024 00:00:00 +0000</pubDate><item><guid>https://patstechweblog.com/posts/5-talk-talk</guid><title>TalkTalk</title><link>https://patstechweblog.com/posts/5-talk-talk</link><description>I was making a programming language but I’m not anymore but maybe I will again.</description><content:encoded><![CDATA[<p>Hey remember when I started a blog and said I was gonna write about stuff? Me neither. Anyways in like July or June I read <a href="https://craftinginterpreters.com">Crafting Interpreters</a> and it broke my brain (in a good way!) so I spent a lil while building a programming language.</p><p>It’s called TalkTalk and it’s not named that because of Charli XCX’s track but actually it is. The idea was a language written in Swift with a Swift-esque syntax but not requiring type annotations that could maybe be used to script Shortcuts or just running in an iOS app for fun. But really the idea was to learn about building a programming language, parsers, compilers, type inference, etc.</p><p>Anyways, you can <a href="https://talktalk.sh">check it out here</a> or on <a href="https://github.com/talktalklang/talktalk">GitHub</a> but I’m sorta over it at this point because I read another book and it broke my brain (in a good way!) again. Maybe in like six months I’ll tell ya about it.</p><p>I kept a little diary while I was working on it, maybe it’ll be interesting for anyone else making their own programming language. So here it is, unedited. Maybe it makes up for not postin’ here.</p><hr><h2>7/6/2024</h2><p>Gonna try to write an AST parser, then generate LLVM IR. Doing the VM + Compiler using C semantics from swift wasn’t working great.</p><h2>7/7/2024</h2><p>Nevermind, we got it working at least. Still like twice as slow as the C implementation.</p><p>Right now properties are just string dict keys. Maybe see if storing as an Int hash value is faster? (eventually we want static types but for now <code>¯\_(ツ)_/¯</code>)</p><h2>7/8/2024</h2><p>Let’s add the ability to access the file system. Maybe sockets (TODO: learn what sockets actually are?)</p><p>Also collection types lol</p><h2>7/10/2024</h2><p>Rewriting the parser to an AST so we can maybe add types?</p><p>Should add some order of operations tests to make sure Pratt is workin</p><h2>7/19/2024</h2><p>LLVM JIT workin, what’s next?</p><p>Workin’ on closures. Right now we’ve just got a heap dictionary in the compiler visitor but names could probably clobber each other which wouldn’t be ideal. Is this what name mangling is for?</p><p>OK we’re starting on an Abstract Binding Tree thing that will be responsible for taking the AST and converting it into a form that’s more appropriate for byte code (including with stuff for closures and environments and scopes and whatnot)</p><p>Use semicolon for comment token? Could make stripping terminators easier plus discourage ending lines with the,<br><br>## 7/28/2024</p><p>Closures workin’ on LLVM. We’re capturing stuff to the heap on func definition then passing that stuff an argument at call sites.</p><h2>8/1/2024</h2><p>Bailed on LLVM. I think it’d be more fun to be able to run this on iOS so it’s back to bytecode VM. Maybe someday we’ll come back to IR-world.</p><p>Sorta made a mess trying to fix up closures and simplify stuff. I think the new approach should be to pass closure objects everywhere at def and call times. Later we can look at optimizing those things away.</p><ul><li>[ ] Strings</li><li>[ ] Classes</li><li>[ ] GC?</li><li>[ ] Hoistin’</li></ul><h2>8/7/2024</h2><p>Closures work.</p><p>We’ve got basic LSP support. Now we gotta work on modules.</p><ul><li>[x] Also the LSP message receiving code needs to actually read the content length because it can get confused sometimes.</li><li>[ ] Completions (still early!) don’t work yet with variables defined outside the immediate local scope.</li></ul><h2>8/9/2024</h2><p>Modules/Structs are getting closer to working.</p><ul><li>[ ] If there’s an unknown identifier error, it’d be nice to be able to suggest a module to import if it exists in the module environment.</li><li>[x] It’d be nice to emit synthetic inits</li></ul><h2>8/10/2024</h2><p>Ok modules and structs work <em>enough</em> (with like a thousand rough edges). Working on the stdlib now. Also sorta using it as an opportunity to try out moving to a new error approach where errors get added to the environment as well as being attached to nodes themselves so we don’t just return ErrorSyntax from the analysis anymore. We’ll see how we like it.</p><p>Made a <em>whole lot</em> of assumptions about things being safe to force unwrap or cast because they made it through parsing that don’t hold with the LSP, since programs can be in invalid state. Need to move away from that pattern going forward.</p><h2>8/11/2024</h2><p>Ok, actually adding arrays now.</p><ul><li>[x] Do a pass over the methods of a struct to make sure their names are available to other methods instead of relying on methods being defined before one another.</li><li>[x] Need to implement while statements in the VM so we can copy stuff over when resizing arrays.</li><li>[x] Comments, let’s add ‘em.</li></ul><h2>8/12/2024</h2><p>Realized I need while loops to implement arrays (to resize an array, we need to copy the existing items to the new one).</p><p>Also missing the <code>+=</code> operator more than I thought I would, might go ahead and add it (or custom operators??) soon.</p><p>Also <code>var</code> and <code>let</code> statements.</p><ul><li>[ ] We’ve got a lot of <code>call</code> functions in the VM right now, there’s def some redundancy that we could clean up.</li></ul><p>Ok I thought we didn’t need “expression statements” because “everything’s an expression” seemed p chill. But I think actually what we want is “everything <em>can</em> be an expression”. By making everything an expression, the stack sorta just gets cluttered up by values we don’t care about. By having expression statements we can pop the values off the stack at the end of the expression. Fiiine.</p><p>This is a sorta gnarly move to expression statements. So many places in the tests to add <code>pop</code> expectations, so many places where we were checking for exprs where we now need to unwrap the expr stmt to get at the creamy crunchy expr, so many places we assumed things were working great because we were leaving the stack one big mess, gonna have to evaluate uses of <code>return</code> to make sure we’re actually leaving stuff on the stack that we should be.</p><p>Also this is interesting reading on implementing generics: https://thume.ca/2019/07/14/a-tour-of-metaprogramming-models-for-generics/.</p><p>Ok totally revamping how type analysis works. Instead of adding types directly to tree nodes we give them a TypeID that can point to a type, but also be updated more easily as well as be shared with other objects that are known to have the same type. This way we should be able to do more inference? Also not worry about nodes being value types?</p><h4>OK.</h4><p>Rewrote the type system to wrap ValueType in TypeID which lets stuff get updated easier. Also introduced ExprStmt (see above). Now we’re popping off the stack <em>too much</em>. Gotta either require explicit returns or adding an explicit return at the end of a thing. Or if there’s only one statement make it a return? So many options.</p><p>For now I think I just force explicit returns just to get tests green again? Then we make it nice. Also add that <code>+=</code> operator everyone keeps talking about.</p><p>Ok Actually it might not be too gnarly to just wrap single statements in a return. It’s like 11pm so everything seems both easy and impossible so let’s try it.</p><p>Ok yea it w</p><p><em>an hour later</em></p><p>Ok yea it wasn’t so bad. Had to special case some ExprStmts (funcs, def expr, structs) (which probably means that they don’t want to be ExprStmts anyway) but we’re back to the same failing tests we had like 12 hours ago so that’s probably a win?</p><p>From wren docs: Sometimes, though, a statement doesn’t fit on a single line and jamming a newline in the middle would trip it up. To handle that, Wren has a very simple rule: It ignores a newline following any token that can’t end a statement.</p><h2>8/13/2024</h2><p>IS TODAY THE DAY? Do we get green again?</p><p>Well at some point maybe. But first we’re gonna add a quick lil ruby script to make it easier to generate new syntax nodes. Because right now it requires changing like five files and there’s no time to do stuff like that. We’re so busy.</p><p>Ok, the script mostly works. It could be nicer (like including properties to generate) but so could everything here.</p><p>Now we’re using the script to add more statements and move stuff away from everything being an expr (RIP struct expr, for now). The implicit return stuff seems to be working?</p><ul><li>[x] Need to make sure implicit returns in a <code>while</code> or <code>if</code> don’t bail out of the function when they shouldn’t...</li></ul><p>Started adding var/let decls because scoping was getting too confusing without those keywords.</p><ul><li>[ ] Need to actually do more validation, like if you try to refer to a var inside a fn that hasn’t been defined, we should add an analysis error.</li></ul><p>Well well well, looks like someone got sidetracked by LSP stuff. Trying to edit Array.tlk and there’s no syntax highlighting. I can’t live like this.</p><p>--</p><p>Ok we’re almost green. Actually implementing <code>Array</code> now.. imagine that. I’m definitely missing implicit <code>self</code> lookups.</p><p>omg we did it. Tests are green. Obsidian needs some sort of confetti thing or something.</p><p>--</p><p>Now what?</p><ul><li>[x] Comments?</li><li>[x] A repl would be nice</li><li>[x] var/let statements</li></ul><h3>Where’d we end up?</h3><p>Super simple arrays work. Comments work. Var statements work. Let statements act just like var statements (for now...). Gonna mess with Strings next I think?</p><p>Oh hell yes, CI is green. Good ol’ build #28.</p><p>Moved init/func decls up into decl() which means we can stop unwrapping ExprStmts in so many places. Phew.</p><ul><li>[ ] One thing we need to do is consolidate errors. I think the overall goal should be to try to get as far as possible and just make sure our types can carry all error or warning info they might need as is appropriate for their stage. Also we should implement panic mode for the parser. Thank you for coming to my ted talk.</li></ul><h2>8/14/2024</h2><p>GOOD MORNING what will we break today?</p><p>What TODOs do we have?</p><ul><li>[x] A repl would be nice</li><li>[x] Need to actually do more validation, like if you try to refer to a var inside a fn that hasn’t been defined, we should add an analysis error.</li><li>[ ] We’ve got a lot of <code>call</code> functions in the VM right now, there’s def some redundancy that we could clean up.</li><li>[ ] If there’s an unknown identifier error, it’d be nice to be able to suggest a module to import if it exists in the module environment.</li><li>[x] Need to make sure implicit returns in a <code>while</code> or <code>if</code> don’t bail out of the function when they shouldn’t...</li></ul><p>Started adding var/let decls because scoping was getting too confusing without those keywords.</p><ul><li>[x] Need to actually do more validation, like if you try to refer to a var inside a fn that hasn’t been defined, we should add an analysis error.</li></ul><p>Well well well, looks like someone got sidetracked by LSP stuff. Trying to edit Array.tlk and there’s no syntax highlighting. I can’t live like this.</p><p>--</p><p>Ok we’re almost green. Actually implementing <code>Array</code> now.. imagine that. I’m definitely missing implicit <code>self</code> lookups.</p><p>omg we did it. Tests are green. Obsidian needs some sort of confetti thing or something.</p><p>--</p><p>Now what?</p><ul><li>[x] Comments?</li><li>[x] A repl would be nice</li><li>[x] var/let statements</li></ul><h3>Where’d we end up?</h3><p>Super simple arrays work. Comments work. Var statements work. Let statements act just like var statements (for now...). Gonna mess with Strings next I think?</p><p>Oh hell yes, CI is green. Good ol’ build #28.</p><p>Moved init/func decls up into decl() which means we can stop unwrapping ExprStmts in so many places. Phew.</p><ul><li>[ ] One thing we need to do is consolidate errors. I think the overall goal should be to try to get as far as possible and just make sure our types can carry all error or warning info they might need as is appropriate for their stage. Also we should implement panic mode for the parser. Thank you for coming to my ted talk. x] Strings</li></ul><p>09:26 should we rename <code>none</code> to <code>nope</code>? or why haven’t we done that already.</p><p>==Ok I think the focus for today is error handling.== We’re gonna go all in on analysis nodes having an <code>analysisErrors</code> field and really try to stop returning ErrorSyntax nodes.</p><p>The amount of extra fields we’ve got hanging off of AnalyzedSyntax is starting to smell like we just need a new object.</p><p>14:37 Ok we have a repl now. I keep putting off doing strings because I feel like I don’t know how to implement them in a “machiney” way. I think for now I just go with Swift strings then move on.</p><p>I think we could maybe take this opportunity to wrap primitive types in classes and have that be a thing.</p><p>17:32 oh baby we have made a mess. Added a String struct and thought “you know what, it’d be nice if string literals just instantiated one of these”. Then “you know what, it’d be nice if int literals just instantiated an Int struct”. But the analyzer couldn’t find the Int struct??? So now we’re in it.</p><p>Oh no. It’s because Int is defined in the standard library. But there are int literals in the standard library. So when the compiler tries to compile the standard library the Int struct isn’t defined yet? I think that’s what’s goin on?</p><p>23:00 Ok we punted on fancy string storage. It’s just a new Value case that holds a swift string.</p><p>Today was bit all over the place (it’s ok.). Maybe tomorrow we can buckle down a bit more. Hopefully.</p><h2>8/15/2024</h2><p>Goooooood morning we have some STOMACH issues today but we’re gonna do our best to make some good progress.</p><p>11:08 Ok the tum is feeling better. Taking a break from language implementation stuff to see if we can get homebrew installation going.</p><p>ok enough of that.</p><p>14:20 Just played with neovim crap for a while, I think it’s time to add more validation and work more on cleaning up errors.</p><ul><li>[ ] Static members would be nice.</li><li>[ ] So would enums with pattern matching</li><li>[x] Let’s start requiring <code>var</code>/<code>let</code> decls before assigning</li></ul><hr><h2>8/16/2024</h2><p>6:55AM Ok we made good progress on validation and LSP stuff yesterday. Also we are <em>this</em> close to having definition support in the LSP server. I think.</p><p>9:02AM We’ve got go to definition working for properties, methods, types, and vars. Need to figure out if it works for cross file/cross module stuff still but this is a p good step at least.</p><p>7:13PM ==Might need to introduce the concept of “mutating”==</p><p>9:43PM Ok cleaned some more stuff up, including making the language server understand the stdlib.</p><ul><li>[x] Need to teach the completer about the standard library (probz by moving it to use the module analyzer instead of just looking in the same file like we did for diagnostics)</li><li>[x] Right now you can <code>let</code> the same var twice in the same scope. We don’t love that.</li></ul><hr><h2>8/17/2024</h2><p>Oh hey. Couple things: trailing block syntax?</p><p>Also SwiftUI wrapper?</p><ul><li>[ ] Visibility? (Public/Private/Module?)</li><li>[x] Need to actually establish and test structs passing by value</li></ul><p>4:22PM We’re cleaning up implicit single statement returns. Previously we were swapping return statements in for expr statements when it was the only statement in a block that allowed it. Now we’ve added a field to expr statement called <code>exitBehavior</code> which I think is a bit tidier.</p><ul><li>[ ] Starting to really want that parser panic mode</li></ul><p>10:23PM Ok callin it here. Right now I’m working through why recursive functions aren’t working. Or when i can get them to work, the counter example isn’t working.</p><h2>8/18/2024</h2><p><strong>8:01AM</strong> Oh where are my manners? Good morning! We’re picking back up on some locals not being set propertly, either in the VM or in the compiler? We’ve got a failing test so let’s crack on.</p><p>Ok we’ve got things narrowed down to named functions.</p><h3>What if we store locals in call frames instead of the stack?</h3><p>Ok I think it’s actually going ok? Everything is actually a bit easier to follow. The one outstanding thing is upvalues.</p><p><strong>1:07PM</strong> Getting closer, I think the only thing remaining is upvalues that live longer than their enclosing scope. We’ll need to shuffle those around. Also the <code>fib</code> test is failing still....</p><p><strong>3:53PM</strong> Ok ok ok. So I think this whole upvalue thing is actually just not what we need to do. We’re not writing this in C, we have Swift. We even have a “fake heap” so if we really wanna fake stuff we can, but on the other hand, if we really want performance we could just actually write it in C or just compile to LLVM. So I think maybe we just bail on this idea altogether.</p><p><strong>10:19PM</strong> Okaaaaaaaaay we made an enormous mess. It was too much. So I backed out of all that and realized I could change <a href="https://github.com/nakajima/talktalk/commit/81f4819b0afc5f7037e65b73c759bc67b987a7a9#diff-1a021f5b5c31a5b7428cb1a9ab38a49fdbf0867614b54415e77dab067b1845a0R318-R342">one line</a> and the original recursion bug was fixed. Oops. But in all my flailing I also realized i had an off by one error in how arguments were being passed to functions so that’s fixed now too.</p><hr><h2>8/19/2024</h2><p><strong>9:30AM</strong> After some profiling last night, our new VM is slower than our old one. That’s ok, we spent a bunch of time optimizing that one. So now let’s optimize this one a lil bit. The first step is moving away from the <code>Chunk</code> class in the VM towards a <code>StaticChunk</code> struct. That should reduce our arc overhead which seemed to be an issue. Before, <code>fib(35)</code> was taking like 28-29 sec, just moving to a <code>StaticChunk</code> for the main chunk was enough to get it down to 20. Now I’m doing away with nested chunks and trying to put them all at the module level so we can simplify stuff more.</p><p><strong>8:04PM</strong> Brought in the Stack implementation from the old talktalk and now we’re doing <code>fib(35</code> in comparable time (like 6-7 seconds). We’re still like 3-4x slower than c-lox but it’s workable.</p><p>Tomorrow let’s get some features goin. Off the top of my head:</p><ul><li>[ ] Subscripts</li><li>[ ] Hashtables</li><li>[ ] Byte arrays</li><li>[ ] Filesystem support</li><li>[ ] Sockets????????? (We still don’t super know what a socket is)</li></ul><h2>8/20/2024</h2><p><strong>7:52AM</strong> Subscripts. Let’s go. Ok also array literals too. Brackets. Let’s go. <s>Let’s go brackets.</s></p><ul><li>[ ] String interpolation would be nice.</li><li>[ ] Stop duplicating type info between ValueType.function and AnalyzedFuncExpr</li><li>[ ] We might want a new MethodDecl instead of trying to re-use func logic in structs</li><li>[ ] Let’s bring back <code>Heap.Pointer</code></li></ul><p><strong>4:38PM</strong> I think we need some sort of notion of implicit method calls. Like how subscripts call a <code>get</code> or <code>set</code> method (for now... I don’t love that convention). Or maybe how a string might have its <code>description</code> field called automatically. Right now there’s a lot of manipulation to try to find these methods. It’d be nice to have a more codified system for it.</p><p><strong>11:20PM</strong> Neil tried talktalk. It was great. He wanted <code>for</code> loops so badly. Also the LSP server crashed constantly. Also he doesn’t know how to use vim but that’s not his fault. He was using <code>let</code> when he meant <code>var</code> and there was no indication that it was wrong. Also wow there is a lot that an LSP can teach. But not when it’s crashed. so we need to fix that.</p><h2>8/21/2024</h2><p><strong>9:53AM</strong> Took a lil drink last night. Feelin a bit slow today. That’s ok. I think the goal for today is pulling all symbol generation into the analysis layer.</p><ul><li>[ ] Visibility? (Public/Private/)</li><li>[x] Add semicolon statement delimiters</li></ul><h2>8/22/2024</h2><p><strong>8:46AM</strong> Ok, we’ve moved symbol generation to the analysis layer (including slot allocation). I think the last thing we’re dealing with at this point is that for imported methods, chunk offsets are wrong.</p><p><strong>11:25AM</strong> Ok yea that’s what going on. For example, the standard library’s Array.append method calls resize() when it needs to resize itself. The location of <code>resize</code> is assumed and emitted during standard library compilation, so those locations are wrong in whatever module we’ve imported into.</p><p>At least I think that’s what going on. Also not entirely sure what to do about it. Maybe pass around symbols until the <em>very</em> last minute?</p><p><strong>12:43PM</strong> That wasn’t it.</p><p><strong>3:09PM</strong> Ok I went back to methods being embedded into Struct objects. All the tests pass. I forget what we were doing.</p><p><strong>10:53PM</strong> Started on Dictionary. Need to beef up generics a lil bit, they’re not being specialized properly. The problem is that before, we just assumed a generic type list was a list of strings inside &lt;&gt;. But they can be nested. We’ve got that parsing but we need to actually make it mean something. Like if you have:</p><pre><code><span class="keyword">struct</span> DictionaryEntry&lt;K, V&gt; {
  <span class="keyword">var</span> key: <span class="type">K</span>
  <span class="keyword">var</span> value: <span class="type">V</span>
}

<span class="keyword">struct</span> Dictionary&lt;Key, Value&gt; {
  <span class="keyword">var</span> storage: <span class="type">Array</span>&lt;<span class="type">DictionaryEntry</span>&lt;<span class="type">Key</span>, <span class="type">Value</span>&gt;&gt;
}
</code></pre><p>Then DictionaryEntry’s <code>K</code> should be set to <code>Dictionary.Key</code> and <code>V</code> set to <code>Dictionary.Value</code>. Right now that’s not working. We’ll get it workin’ tomorrow.</p><h2>8/23/2024</h2><p><strong>8:09AM</strong> Let’s get on those generics.</p><p><strong>10:13AM</strong> Oh boy is this the day we solve the duplication between the AST and the ValueType function types?</p><p>I think maybe the safest thing to do would be to just start using the AST types more instead of just deleting a bunch of stuff? Then gradually moving stuff over? Maybe?</p><p><strong>11:10AM</strong> Started pulling larger SourceFileAnalyzer visit methods into their own structs. Not sure why I’m telling you about it here but it seems like a good idea.</p><h2>8/24/2024</h2><p><strong>7:08AM</strong> I think we need to use our</p><p><strong>7:26PM</strong> I didn’t forget about you. Lol it’s been 12 hours. Ok well we’re still working on dictionaries. Right now the problem is that our type inference is <em>too powerful</em>. When we pass an argument to a parameter that has a placeholder generic type (say, <code>Array.append</code>), it’s updating that parameter type ID globally, so later when we try to pass it an <code>int</code> it blows up. So we need to make these constraints scoped to the call site or instance or something.</p><p><strong>11:27PM</strong> Ok we have a couple issues:</p><ul><li>[ ] Properties aren’t being resolved properly. Dictionary sometimes (it’s the <em>sometimes</em> that is maddening) doesn’t have any properties defined when we’re in MemberExprAnalyzer so <code>self.storage</code> returns an error. Might be related to...</li><li>[ ] Parameter inference is going too hard. We need to figure out a more local scope or just bail? I think we should maybe take a break and try to implement hindley milner on our own.</li></ul><h2>8/25/2024</h2><p><strong>8:10AM</strong> Ok, let’s try a new type checker.</p><p><strong>10:14AM</strong> We have created a new package. We have also added a new <code>id</code> field to <code>Syntax</code> so that we have a more reliable way of identifying fields. That meant that all of our analyzed types needed an <code>id</code> field which should have been easy since they all have a wrapped syntax node but the way we were doing that was sorta ad hoc. So we added a <code>wrapped</code> field to <code>AnalyzedSyntax</code> which points to a concrete syntax type. So we needed to update all the analyzed nodes that had an ad hoc <code>expr</code> or <code>stmt</code> field to use <code>wrapped</code> and point to a generic syntax node. Then we needed to go and update all the call sites where we created those nodes. It was fine.</p><p>Time to try to type check some very basic expressions.</p><h2>8/26/2024</h2><p><strong>11:05AM</strong> Late start today! That’s alright, we’re on a good track with this HM stuff (F A M O U S L A S T W O R D S). Gonna try to get more of this struct stuff workin’.</p><p><strong>11:37PM</strong> lol 12 hours again. Ok we’ve got expressions checked and normal structs. But generics are a problem. I think maybe introducing a separate StructType and Instance type was a mistake and I should just make it so that we use Scheme as a StructType sort of thing that can <em>instantiate</em> instances? Need to figure out how property access works in that case but it seems like it might be better than these two concepts that feel too close together? We’ll see how it feels in the morning.</p><h2>8/27/2024</h2><p><strong>8:49AM</strong> The horrible parrots sing in the sky!</p><p><strong>9:40AM</strong> We’re trying out claude.ai</p><h2>8/28/2024</h2><p><strong>8:43AM</strong> We’re kinda slacking on the note taking. I think that’s because we’ve been flailing so much more? I don’t know. Either way, still working on type inference. We’ve moved to more of a constraint based setup. Initially we were going willy nilly on generating type variables and trying to unify them everywhere which was kinda going off the rails. Now we’re going to try to avoid creating them unless we need to.</p><p>It <em>is</em> nice to have the inference visitor just be responsible for generating constraints and picking out lil bits of type info that we know to be true, then letting the solving step happen elsewhere. So that’s a win.</p><h2>8/30/2024</h2><p><strong>9:47AM</strong> What the heck, we didn’t blog at all yesterday! Our readers will be up in arms. Apologies.</p><p>Welp we have now run our type checker tests at least once and they were all green. I’m sure there’s probably some more bugs lurking but it was nice to see.</p><p>I guess the next step is actually wiring it up to the analyzer.</p><p>Deleting a buttload of code from the analyzer, I’m sure it’s fine. I think the main things we need to make sure it keeps are:</p><ul><li>[x] Captures</li><li>[x] Pointers?</li><li>[x] Exit behavior</li><li>[ ] Make sure locations are still working for Definitions and the LSP</li><li>[x] Mutability checks for var/let</li><li>[ ] We need to hoist member functions</li></ul><p><strong>9:29PM</strong> Not a bad day’s work. We’ve got source file analyzer tests green again with the new inference system. I think tomorrow it’s on to the compiler and maybe LSP. Also probably find a bunch of other broken stuff, but at least it feels like we’re on the right track.</p><p>Seems like we’re probably gonna have to modify the inferencer to allow more stuff in a broken state for the LSP?</p><h2>8/31/2024</h2><p><strong>8:22AM</strong> Here we go again! Feels good to run all of the source file analyzer tests and have them pass.</p><p>Had to bring back global detection in var/let decls.</p><p><strong>6:56PM</strong> Made a bunch of progress, gettin stuff green today. I think each module should probably be responsible for how it handles the standard library instead of trying to somewhat unify them in the driver. The driver can probably go away tbh, I was just sorta copying Swift and didn’t know what I was doing (as opposed to now, when I <em>definitely</em> know what I’m doing.)</p><p><strong>9:35PM</strong> We’ve made it to the VM tests. Time to debug stack stuff again, oh boy oh boy oh boy.</p><p><strong>11:20PM</strong> We are lookin not bad. Let’s call it here.</p><h2>9/1/2024</h2><p><strong>8:04AM</strong> wow September. We’re goin to vegas today so not sure how much work will get done but for now the plan is to get the analysis error and find definition tests green, then we can go back to banging out head against the stack in the VM tests.</p><p><strong>10:32AM</strong> Ok we’re green (kinda!). The factorial (recursion) type inference test is flakey as heck. Probably need to look into that but gonna merge for now.</p><p><strong>1:26PM</strong> TURBULENCE. Ok we’ve started on an iOS app. Back to our roots. As of like a couple years ago. Realizing we need to probably not just print straight to standard out for print.</p><p>I think highlights are borking our selection.</p><h2>9/2/2024</h2><p><strong>11:43AM</strong> I didn’t forget about you! Yes I did but I’m here now. I’m hacking on the iOS app. I think we just don’t worry about Mac interop since all the functionality should be shared by the LSP so on mac you can just use nvim (or VSCode once we make an extension for it).</p><p>I think the top two things we need atm for the iOS app are output capture (in the VM) and pretty formatting of talktalk values (in the iOS app).</p><p><strong>2:31PM</strong> Ok we need to handle runaway processes (fib(35) isn’t a good look.) Probably memory issues too</p><h2>9/3/2024</h2><p><strong>12:26PM</strong> Whoops how are we doing. Still workin’ on the iOS app but it looks like our VM has another stack imbalance issue. Whoops.</p><p>Could be fun to play with refinements in the type system. Also adding length to array types?</p><p><strong>5:23PM</strong> I think we’re gonna need to go through and completely remove fatal errors and forces. First of all because we never want the iOS app to crash, second because we should really just be handling this stuff.</p><h2>9/4/2024</h2><p><strong>2:50PM</strong> Just chillin, workin’ on showing diagnostics in the iOS app.</p><p><strong>10:15PM</strong> Ok we have started on enums and pattern matching. The basics are in the parser so I think tomorrow we start on the type inference side of things.</p><h2>9/5/2024</h2><p><strong>10:07AM</strong> We’re doin this and that. Adding enum/pattern matching to the inferencer.</p><ul><li>[x] Also string interpolation would still be nice.</li></ul><p><strong>9:27PM</strong> Ok, we’re good (probably!) on inferring match statements. I think tomorrow we get goin on the analysis side. Maybe compiler? Maybe VM? Maybe iOS? Maybe string interpolation?</p><h2>9/6/2024</h2><p><strong>8:20AM</strong> ANALYSIS. Let’s do it.</p><p><strong>10:25AM</strong> We need to make sure generics work with enums. Fun!</p><ul><li>[x] Need to add exhaustivity checks for matches</li></ul><h2>9/7/2024</h2><p>Travel day today, and party day. But I think we just wanna keep goin on c</p><h2>9/8/2024</h2><p><strong>12:06PM</strong> Another travel day! What the heck?? Ok I had this weird idea but what if patterns are just function calls that return a pattern or something? Or maybe each case body is a chunk? That seems like it could simplify things.</p><p><strong>12:49PM</strong> Gonna take a crack at simplifying the VM before we keep going on enums. That means less stack cleverness.</p><p><strong>9:18PM</strong> Ok, compiler tests are green again. Gotta implement it in the VM now.</p><h2>9/9/2024</h2><p><strong>8:51PM</strong> Oh hi. We landed the VM simplification today. Also cleaned up some return stuff (we now distinguish between returnValue and returnVoid in the opcodes and VM). Now we’re back on enums. I think we can simplify things a bit by using member getters instead of trying to parse cases out so much. We’ll see.</p><p><strong>9:28PM</strong> Need to figure out how pattern matching is gonna work.</p><h2>9/10/2024</h2><p><strong>8:42AM</strong> Patterns! What? Match--- Ok.</p><p><strong>2:25PM</strong> Ok we have added <code>and</code> support to the VM tho we should probably get around to adding it to the parser. Besides that... boy... pattern matching... I don’t know.</p><p>10:01PM Ok, we’ve got us some pattern matching. The tests are all green?? But we don’t have enough tests. Specifically we’re missing:</p><ul><li>[x] Nested patterns aren’t working yet</li><li>[x] Enums referred to before their definition aren’t working yet</li><li>[x] We need to make new frames or something to ensure variables don’t leak outside their match statement bodies</li><li>[x] It’d be nice to get some exhaustivity checks going</li></ul><p><strong>10:33PM</strong> Bed time. But we have added some nice failing tests for tomorrow.</p><h2>9/11/2024</h2><p><strong>5:20PM</strong> Hey hey. We’ve got our tests green again, including nested patterns.</p><p><strong>9:58PM</strong> Ok so we’re not done yet. We weren’t applyingSubstitutions properly on enum cases, which led us to fix that. But then that started leaking enum generic types in their cases. So we’ve introduced enum case <em>instance</em> to the mix. It works similarly to struct instances in that it can have its own substitutions for generic types and leave the normal EnumCase values alone. So we just have to finish wiring that up I think. It’s a unix system, we know this.</p><h2>9/12/2024</h2><p><strong>3:48PM</strong> Hey ho. We bailed on enum case instances. I forget how tbh but all the tests are green again. Also we changed how match statements work to just create a new chunk which means we don’t leak variables anymore. Huzzah.</p><p><strong>5:48PM</strong> Ok just fixed up variables leaking outside their scope so I think we’re in a good spot? Truly impossible to know. But I guess we can merge this PR.</p><p>Ok we merged. Maybe we mess around with strings for a bit? Could be nice to have escapes as well as interpolation.</p><h2>9/13/2024</h2><p><strong>7:53AM</strong> Time to <em>oh no it’s friday the 13th spoooooky</em> keep goin on string interpolation. I think the lexer works, so now we gotta parse these babies.</p><p><strong>10:29PM</strong> Well we got string interpolation in. Now we’re workin’ on a proper formatter.</p><h2>9/14/2024</h2><p><strong>5:35PM</strong> We got a formatter. And it’s <code>.talk</code> now. I think maybe we play with homebrew now.</p><h2>9/15/2024</h2><p><strong>11:24AM</strong> G’day mate! Let’s throw another shrimp on the blarney! Homebrew is <em>sorta</em> working, though the account we made on github to do automated releases got flagged lol.</p><p>Maybe it’s time to start on protocols in earnest?</p><h2>9/16/2024</h2><p><strong>10:01PM</strong> G’night mate! We’ve been workin’ on protocols. You can define them with requirements, we can check that structs implement those conformances, you can pass them around as args. Neat!</p><p>We’ve also added methods to enums. And made a big mess but we cleaned it up. I sort of wonder if the Analysis layer is more trouble than it’s worth, now that we have the TypeChecker. I think all it really does at this point are:</p><ul><li>[ ] Symbol generation (do we even need this?)</li><li>[ ] Additional validation (the type checker could do this)</li><li>[ ] Match statement exhaustivity checking</li><li>[ ] It’s what the compiler consumes (but it could just consume AST? Or we could have a more deliberate IR?)</li></ul><p>So who knows? Might be worth exploring ripping it out at some point. There’s so much redundant info between that and the type checker.</p><p><strong>10:23PM</strong> Ok, I think we want to actually have enum instances. Or something. We’re struggling Optional right now and I think the other enum generics tests are passing just because it’s changing the global type each time. Or maybe it’s just storing stuff in other weird spots or something. I dunno let’s look tomorrow.</p><h2>9/17/2024</h2><p><strong>7:37AM</strong> What were we doing? At some point we should probably write a blog post or something about talktalk. That’ll show them, that’ll show ALL of them. That probably means we need to add syntax highlighting for HTML for talktalk somewhere. Neat.</p><p>But first let’s finish this round of protocols. I think that means:</p><ul><li>[x] Making sure enums can conform</li><li>[x] Associated types</li></ul><p>Oops, we’re starting on for loops in here. I guess that’s fine since that’s why we started on protocols to begin with but it probably would have been a better idea to do them in a follow up. But also how else are we gonna drive associated types without them? Some sort of FAKE situation? Not on my watch. No let’s just make this PR enormous.</p><p><strong>1:38PM</strong> tfw you realize you have to implement <em>optionals</em> for the <code>next()</code> method you want to use for <em>for</em> loops that you’re implementing to drive out your silly <em>protocols</em> feature.</p><p>==Oops found an issue. In CallConstraint.solveStruct, we’re calling .member() to see if there’s an init defined but it never returns one. If we change it to return something, it breaks SO MUCH STUFF. ==</p><p>Wait if we wanna do dictionary iteration do we need tuples?</p><p>Why is this feature EXPLODING.</p><h2>9/18/2024</h2><p><strong>8:07AM</strong> Ok let’s get generic enums fixed up. Then maybe we look at optimizing Symbol? It can just be a string under the hood and all the members could just be methods rather than having enum cases with multiple members.</p><p>Hmmm. hmm. Can we actually consolidate struct instance/enum instance? Treat cases as just members on a struct instance? Could be nice. We’ve got coffee. Let’s try.</p><p>Wait could we also consolidate all Instantiables? Let’s maybe try that next?</p><p><strong>9:15AM</strong> OK we are trying it. The <code>.instance()</code> consolidation went like... too smoothly? Everything was just green. Neat. Now we’re onto consolidating all instantiatable types, which is a bit more intrusive but I think is gonna be good.</p><p><strong>9:39AM</strong> Huh we’re green there now too. I think <code>Instantiatable</code> is maybe not the <em>best</em> name since protocols and enums aren’t actually <em>instantiatable</em>, but we can cross that bridge later.</p><p>Programming is just making one decision on purpose that makes like 5 by accident.</p><p>Ok, I think we need to update our pattern handling stuff. I think maybe the pattern visitor is too much?</p><p><strong>10:35PM</strong> Ok bed time. We’re reworking a lot of the pattern handling stuff. Fresh eyes tomorrow will probably be helpful but we <em>are</em> keeping the pattern visitor for now. The pattern matching tests are green (heck yea) but the optionals and for loop tests are broken (boohoo). Ohno and the dreaded “infer generic enum” test is failing too.</p><h2>9/19/2024</h2><p><strong>7:32AM</strong> Okey doke where were we? Looks like the changes we ended up with last night broke generic handling in patterns. So let’s take a look. it’s a me, mario.</p><p><strong>12:00PM</strong> Ok I think we’ve isolated the last problem with generic enums. It’s because <code>solveEnumCase</code> in the call constraint was ignoring args. Ok so let’s just unify the attached types with the args? Not so fast wise guy. That unfortunately unifies them everywhere. We only want it to be on an enum case instance. But we don’t have enum case instances. And last time we tried to add them we made a MESS. But maybe this time will be different? It <em>does</em> seem like enum case should be <code>Instantiatable</code>. So maybe? At least we can always roll back to <code>d2322d0</code> if we get into trouble.</p><p><strong>5:02PM</strong> Ok we got a bunch of stuff cleaned up but we’re still failing on using a func call as a match target. I think we might need to move a lot of this stuff in PatternVisitor to constraints.</p><h2>9/20/2024</h2><p><strong>8:29AM</strong> Well we go the tests green. Let’s see what we missed.</p><ul><li>[ ] ==Lol we need to compile for stmts==</li><li>[ ] Method extensions</li><li>[ ] Property extensions???</li><li>[ ] We should make sure <code>continue</code> and <code>break</code> work.</li><li>[ ] Wildcard patterns</li><li>[ ] Gonna probably need nested conformances? So if <code>protocol A</code> conforms to <code>protocol B</code>, then <code>struct S</code> needs to conform to both A and B in order to conform to A.</li><li>[ ] It’d be nice to cleanup how params/args are handled, including unnamed arguments with the <code>_</code> stuff like in Swift.</li><li>[ ] Parser recovery/panic</li><li>[ ] Dictionaries need finishing</li></ul><p>SO of course thing to do is embark upon killing the analysis layer.</p><p>Maybe the way it can work is that we just visit the AST and save info to the side (like node comments in the formatter) rather than creating new nodes?</p><p>Ok so what are all the things the analysis layer does that inference doesn’t?</p><ul><li>[ ] Analysis nodes are consumed by the compiler. That wouldn’t be too tough to swap out for normal syntax nodes</li><li>[ ] Hold inference type information. We already have that due to the inference layer</li><li>[ ] Generates symbols for the compiler. Could the compiler do this on the fly?</li><li>[ ] Perform exhaustivity checks for match statements</li><li>[ ] Provides LSP location lookup info</li><li>[ ] Coordinates imports</li><li>[ ] Determines exprstmt exit behavior (pop/return/none)</li><li>[ ] Determines captures? Or does the compiler just handle this?</li><li>[ ] Synthesizes inits</li></ul><p>What is being duplicated</p><ul><li>[ ] The entire AST tree</li><li>[ ] All of the type information gathered by the type checker</li></ul><p>Ok if we’re gonna do this we should go test by test in the analysis tests and make sure we have one in either compiler or type checker that tests the same thing.</p><p><strong>11:06AM</strong> Ok after a couple hours of digging into, I don’t think it seems worth it for now.</p><p><strong>4:39PM</strong> We had such big aspirations this morning. Well that went out the window which is ok. You’re ok. Anyways, we finished up for loops (Probably!) since we forgot to actually analyze/compile them last time. Oops. Of course no compiler/vm feature would be complete without juggling shuffling a bunch of stack logic so we did that too. Tests are green again though!</p><h2>9/22/2024</h2><p><strong>3:38PM</strong> We didn’t blog yesterday! Holy moly. Well we’ve got a website. It runs talktalk in webassembly which I think is p neat.</p><h2>9/23/2024</h2><p><strong>7:55AM</strong> Let’s try giving funcs their own stacks. Just to see what it’s like.</p><p><strong>8:12AM</strong> That didn’t feel cozy.</p><p>Ok i think we want some IO. I think it might be nice to provide a couple static funcs on an IO object. So we need static funcs. Or do we do method extensions on protocols first? Static funcs i think? Or do we clean up LSP messages for protocol nonconformance? Hmm.</p><p>Also still need dicts lol.</p><p><strong>4:03PM</strong> Ok, we’ve started on static members. Might be time to revisit how methods work. I think we might just want to pass the receiver to the function as the first arg? That’ll make it easier to compile for LLVM or whatever.</p><h2>9/26/2024</h2><p><strong>9:54AM</strong> ok we took some time off to make some cash money. Where were we? OH yea what’s goin on with static funcs?</p><h2>9/27/2024</h2><p><strong>6:24PM</strong> Ok we spent some time makin’ stuff quicker. Let’s tackle some odds and ends:</p><ul><li>[x] Parse +=/-= as its own thing</li><li>[x] &amp;&amp; and ||</li></ul><h2>9/28/2024</h2><p><strong>8:50AM</strong> Let’s get dicts goin.</p><p><strong>12:44PM</strong> I wonder how much slower (if at all) a purely interpreted version of talktalk is.</p><p><strong>10:39PM</strong> We started on dicts, we need to make the LSP better to work on the actual dict algo. We’re losing comments in the formatter.</p><h2>9/29/2024</h2><p><strong>10:20AM</strong> Ok we’re still on dicts. We did a bunch of other stuff. But now to implement the hash table lookup, it’d be nice to have doubles for calculating the capacity load?</p><ul><li>[ ] Looks like for loops aren’t working for generic arrays.</li><li>[ ] Multiple string interpolations aren’t working?</li><li>[ ] We’re not really doing array bounds checks properly</li></ul><p><strong>3:48PM</strong> ohno. I think we need to redo our type checker (again). We need to move away from any sort of actual inference happening in the visitor and have it <em>only</em> generate constraints.</p><p><strong>9:35PM</strong> we have avoided the impulse to rewrite the type checker (again). We’ve also introduced a new Pattern syntax type that means our parser can start to do some work for patterns instead of having to overload call expressions. We’re starting with <code>if let</code> statements but if that feels good we can look at introducing it to match statements.</p><h2>9/30/2024</h2><p><strong>10:10AM</strong> OK! I think we need to make constraints scope aware. Oops.</p><h2>10/1/2024</h2><p><strong>8:36AM</strong> Wow it’s october. Who knew? Ok we are redoing the type checker. Sorta. We’re going to be more aware of contexts. So instead of having one big context, full of bad types, we’re going to model contexts as a tree that corresponds to scopes. The impetus for this was that we were having trouble with <code>if let foo</code> statements, since we only wanted <code>foo</code> to be defined in the consequence block but it was leaking.</p><p>Hopefully we can be more deliberate about constraints here too.</p><p><strong>1:39PM</strong> Ok brb but right now we’re moving .function() to have inference types instead of results.</p><p><strong>8:24PM</strong> ok the problem is that we define the parameter values for a function as fresh type variables. Then we save a scheme that has those free variables. Then we visit the function body but it uses the original variables. So we never unify what we learn from bodies with the parameters (and vice versa).</p><h2>10/2/2024</h2><p><strong>9:54PM</strong> We made p solid progress today on the new type checker. We’ve yet again reached generics. I think the current problem is that we’re not respecting explicit type parameters, so for example <code>struct Inner&lt;InnerWrapped&gt;</code> getting set with <code>Inner&lt;Foo&gt;</code> is ignoring <code>Foo</code> and just spawning new type variables.</p><h2>10/5/2024</h2><p><strong>6:59AM</strong> FORGOT TO BLOG AGAIN. It’s ok we were making good progress. We got a bunch of core stuff goin, now it’s time to get into optionals..</p><p><strong>9:53PM</strong> Hey hey. So we got the new type checker green and deleted the stuff from the old. Now we’re trying to get the Analysis tests green again. Once that happens I think we can actually go through and strip out a bunch of redundant stuff from analysis. At some point it would be lovely to be able to remove it entirely but in the mean time we can kill stuff like type exprs and whatnot since they aren’t needed past type checking.</p><h2>10/6/2024</h2><p><strong>8:28AM</strong> Hey chat, we’re replacing the InferenceType enum with a protocol so that types can be more than enum cases. It’s gonna make pattern matching strictly worse but give us a place to hang source location info and bound types.</p><h2>11/13/2024</h2><p>We’ve moved on to something else. But we’ll be back</p>]]></content:encoded><pubDate>Wed, 13 Nov 2024 00:00:00 +0000</pubDate></item><item><guid>https://patstechweblog.com/posts/4-swift-server-data-talkin-to-the-database</guid><title>Swift_Server_Data: Talking to the database</title><link>https://patstechweblog.com/posts/4-swift-server-data-talkin-to-the-database</link><description>Wrapping SQLKit for fun and absolutely no profit</description><content:encoded><![CDATA[<small style="text-align: center; display: block;"><em>This is part 2 of a series of posts about writing a SwiftData-esque server side framework for fun.<br/>You can <a href="https://patstechweblog.com/posts/3-swift-server-data">read part 1 here</a> or <a href="https://github.com/nakajima/ServerData.swift">check it out on GitHub</a>.</em></small><p>Previously on patstechweblog: I was flailing my way through Swift macros in order inspect my model’s properties. These <a href="https://github.com/nakajima/ServerData.swift/blob/main/Sources/ServerDataMacros/ModelMacro.swift">macros let me create column definitions</a> that look like this:</p><pre><code><span class="comment">// Contains information about a column. This gets populated from the
// @Model macro, along with some metadata from the @Column macro which
// doesn't do anything besides sit there waiting to be parsed by swift-syntax.</span>
<span class="keyword">public struct</span> ColumnDefinition: <span class="type">Sendable</span> {
  <span class="keyword">public var</span> name: <span class="type">String</span>
  <span class="keyword">public var</span> sqlType: <span class="type">SQLDataType</span>?
  <span class="keyword">public var</span> swiftType: <span class="type">Any</span>.<span class="type">Type</span>
  <span class="keyword">public var</span> isOptional: <span class="type">Bool</span>
  <span class="keyword">public var</span> constraints: [<span class="type">SQLColumnConstraintAlgorithm</span>]
}
</code></pre><p>The macro stores these on the model as a static property called <code>_$columnsByKeyPath</code>. So how do we turn those into a database table? With SQLKit:</p><pre><code><span class="comment">// The @Model macro adds conformance to StorableModel</span>
<span class="keyword">public extension</span> <span class="type">StorableModel</span> {
  <span class="comment">// This is defined to get around macro expansion ordering issues. We should never
  // see this actually happen.</span>
  <span class="keyword">static var</span> _$table: <span class="type">String</span> { <span class="call">fatalError</span>(<span class="string">"should have been expanded by the macro"</span>) }

  <span class="keyword">static func</span> create(in database: <span class="keyword">any</span> <span class="type">SQLDatabase</span>) <span class="keyword">async throws</span> {
    <span class="comment">// Create a SQLKit `SQLCreateTableBuilder` that we'll use to create the table.</span>
    <span class="keyword">var</span> creator = database.<span class="call">create</span>(table: _$table)

    <span class="comment">// Go through each of the columns that we got from the @Model macro</span>
    <span class="keyword">for</span> column <span class="keyword">in</span> _$columnsByKeyPath.<span class="property">values</span> {
      <span class="comment">// Copy all the constraints that were defined with the @Column macro</span>
      <span class="keyword">var</span> constraints = column.<span class="property">constraints</span>

      <span class="comment">// If the property isn't optional, add a not null constraint into the DB as well</span>
      <span class="keyword">if</span> !column.<span class="property">isOptional</span> {
        constraints.<span class="call">append</span>(.<span class="dotAccess">notNull</span>)
      }

      <span class="comment">// Just assume that the primary key is called `id`. Works well enough in Rails.</span>
      <span class="keyword">if</span> column.<span class="property">name</span> == <span class="string">"id"</span> {
        constraints.<span class="call">append</span>(.<span class="call">primaryKey</span>(autoIncrement: <span class="keyword">true</span>))
      }

      <span class="comment">// If we specified an explicit SQL type with @Column, use that for the DB.
      // Otherwise, check to see what the column looks like it could be stored as.</span>
      <span class="keyword">let</span> type: <span class="type">SQLDataType</span> = <span class="keyword">if let</span> sqlType = column.<span class="property">sqlType</span> {
        sqlType
      } <span class="keyword">else</span> {
        <span class="keyword">switch</span> column.<span class="property">swiftType</span> {
        <span class="keyword">case is any</span> <span class="type">StorableAsInt</span>.<span class="type">Type</span>: .<span class="dotAccess">bigint</span>
        <span class="keyword">case is any</span> <span class="type">StorableAsDouble</span>.<span class="type">Type</span>: .<span class="dotAccess">real</span>
        <span class="keyword">case is any</span> <span class="type">StorableAsText</span>.<span class="type">Type</span>: .<span class="dotAccess">text</span>
        <span class="keyword">case is any</span> <span class="type">StorableAsData</span>.<span class="type">Type</span>: .<span class="dotAccess">blob</span>
        <span class="keyword">case is any</span> <span class="type">StorableAsDate</span>.<span class="type">Type</span>: .<span class="call">custom</span>(<span class="type">SQLRaw</span>(<span class="string">"DATETIME"</span>))
        <span class="keyword">default</span>:
          <span class="call">fatalError</span>(<span class="string">"cannot represent:</span> \(column.<span class="property">swiftType</span>)<span class="string">"</span>)
        }
      }

      <span class="comment">// Add the column to the builder.</span>
      creator = creator.<span class="call">column</span>(column.<span class="property">name</span>, type: type, constraints)
    }

    <span class="comment">// When we're done, create the table!</span>
    <span class="keyword">try</span>! <span class="keyword">await</span> creator.<span class="call">run</span>()
  }
}
</code></pre><p>SQLKit’s <a href="https://github.com/vapor/sql-kit/blob/main/Sources/SQLKit/Builders/Implementations/SQLCreateTableBuilder.swift"><code>SQLCreateTableBuilder</code></a> is doing the heavy lifting here. It abstracts the database-specific statements away into <a href="https://github.com/vapor/sql-kit/blob/main/Sources/SQLKit/Database/SQLDialect.swift">dialects</a>, which can be MySQL, Postgres or SQLite. How do we tell ServeData about database? That’s the job of the Container. The Container wraps our specific database adapter and makes it available to ServerData.</p><p>Here’s how we set one up for MySQL:</p><pre><code><span class="keyword">let</span> databaseName = <span class="string">"cool_development_database"</span>
<span class="keyword">var</span> configuration = <span class="type">MySQLConfiguration</span>(
  url: <span class="type">ProcessInfo</span>.<span class="property">processInfo</span>.<span class="property">environment</span>[<span class="string">"MYSQL_URL"</span>]!
)!

configuration.<span class="property">database</span> = databaseName

<span class="keyword">let</span> source = <span class="type">MySQLConnectionSource</span>(configuration: configuration)
<span class="comment">// Ostensibly you've have another way of getting an event loop group in an actual app,
// but this is how I do it in tests.</span>
<span class="keyword">let</span> pool = <span class="type">EventLoopGroupConnectionPool</span>(
  source: source,
  on: <span class="type">MultiThreadedEventLoopGroup</span>(numberOfThreads: <span class="number">2</span>)
)

<span class="keyword">let</span> mysql = pool.<span class="call">database</span>(logger: <span class="type">Logger</span>(label: <span class="string">"test"</span>))

<span class="keyword">let</span> container = <span class="keyword">try</span> <span class="type">Container</span>(
  name: databaseName,
  database: mysql.<span class="call">sql</span>(),
  shutdown: { pool.<span class="call">shutdown</span>() } <span class="comment">// Called on Container.deinit</span>
)
</code></pre><p>So what can we do with the Container? All sorts of stuff really. Like create a PersistentStore for a model:</p><pre><code><span class="keyword">let</span> store = <span class="type">PersistentStore</span>(for: <span class="type">Person</span>.<span class="keyword">self</span>, container: container)

<span class="comment">// Create the database table if it doesn't exist already</span>
<span class="keyword">await</span> store.<span class="call">setup</span>()
</code></pre><p>Now that we have a PersistentStore, we can start saving records:</p><pre><code><span class="comment">// Insert a person into the people table</span>
<span class="keyword">let</span> person = <span class="type">Person</span>(age: <span class="number">40</span>, name: <span class="string">"Pat"</span>)
<span class="keyword">try await</span> store.<span class="call">save</span>(person)

<span class="comment">// Insert a person into the people table and set its ID to
// the new record's ID</span>
<span class="keyword">var</span> person = <span class="type">Person</span>(age: <span class="number">40</span>, name: <span class="string">"Pat"</span>)
<span class="keyword">try await</span> store.<span class="call">save</span>(&amp;person)

<span class="comment">// Insert a bunch of people into the people table</span>
<span class="keyword">var</span> people = [<span class="type">Person</span>(age: <span class="number">40</span>, name: <span class="string">"Pat"</span>),  <span class="type">Person</span>(age: <span class="number">50</span>, name: <span class="string">"Not Pat"</span>)]
<span class="keyword">try await</span> store.<span class="call">save</span>(people)
</code></pre><p>Believe it or not, we can also list records:</p><pre><code><span class="keyword">let</span> people = <span class="keyword">try await</span> store.<span class="call">list</span>()
</code></pre><p>We can use a Swift <code>Predicate</code> that generates SQL to add conditions as well (the following examples actually escape the values but I’ve removed that for simplicity here):</p><pre><code><span class="comment">// SELECT * FROM people WHERE `name` = "Pat"</span>
<span class="keyword">let</span> people = <span class="keyword">try await</span> store.<span class="call">list</span>(where: #Predicate {
  $0.<span class="property">name</span> == <span class="string">"Pat"</span>
})

<span class="comment">// SELECT * FROM people WHERE COALESCE(`id`, -1) &gt; 0</span>
<span class="keyword">let</span> people = <span class="keyword">try await</span> store.<span class="call">list</span>(where: #Predicate {
  ($0.<span class="property">id</span> ?? -<span class="number">1</span>) &gt; <span class="number">0</span>
})

<span class="comment">// SELECT * FROM people WHERE `age` &gt; 30 AND `name` = "Pat" OR `name` = "Pat"</span>
<span class="keyword">let</span> people = <span class="keyword">try await</span> store.<span class="call">list</span>(where: #Predicate {
  $0.<span class="property">age</span> &gt; <span class="number">30</span> &amp;&amp; ($0.<span class="property">name</span> == <span class="string">"Pat"</span> || $0.<span class="property">name</span> == <span class="string">"Not Pat"</span>)
})
</code></pre><p>Obviously it doesn’t make sense to use a Predicate for all cases, but it can be nice while prototyping to have type-safe, SQL-escaped queries. We can also just use SQLKit directly if we want to query the DB with our own SQL.</p><hr><p>So that’s the basic in and outs of getting things in and out of the database. Next time we’ll talk about how the Predicate stuff works. The <code>#Predicate</code> macro in SwiftData is neat and weird and magical and it was fun to learn how it works.</p>]]></content:encoded><pubDate>Fri, 17 May 2024 00:00:00 +0000</pubDate></item><item><guid>https://patstechweblog.com/posts/3-swift-server-data</guid><title>Swift_Server_Data</title><link>https://patstechweblog.com/posts/3-swift-server-data</link><description>I mean why not?</description><content:encoded><![CDATA[<small style="text-align: center; display: block;"><em>This is the first of what I’m sure will be a few posts detailing <a href="https://github.com/nakajima/ServerData.swift">a fun lil thing I’m trying to do</a>.</em></small><p>I was working on an iOS thing for fun and decided it probably wanted a server component. My first thought was oops. Then it was, “hmm what do I write this in?” I landed on Swift because I like Swift<sup><a id="footnote-link-1" href="#footnote-1">1</a></sup>. I landed on MySQL because I know it.</p><p>I spun up a little <a href="https://github.com/hummingbird-project/hummingbird">Hummingbird</a> server then started looking at persistence options. I found a bunch of great options but none really seemed to work the way I wanted and I thought I could just put something together quickly (oops). Cut to me writing my own.</p><p>After using SwiftData in the iOS side, I liked a lot about how it felt to use. I didn’t super know how it worked besides something something macros something something predicates. So I thought it’d be fun to make a version of it for the server.</p><p>Servers are different than clients though. So here are my sort of high level goals:</p><h4>Be Safe</h4><p>I want to be Sendable. When I go, I want everyone to talk about me like “wow pat was so sendable.” So I wanted to use structs because they make that easier.</p><p>Another thing I wanted was type-safe queries. I know generating queries is <em>fraught</em> but so is me writing SQL. As long as there’s an escape hatch for writing raw queries, I think we should be good.</p><h4>Not worry about observation</h4><p>A lot of what makes SwiftData nice on the client is that updates to the store can update views easily<a href="/posts/2-live-model/">-ish</a>. On the server I don’t care about that, which is nice because a <em>downside</em> of SwiftData is that all your model objects are classes. Reference semantics can make sense for models but after using stuff like <a href="https://github.com/groue/GRDB.swift">GRDB</a> and playing with <a href="https://github.com/marcoarment/Blackbird">Blackbird</a>, I think using structs is nicer.</p><h4>Automigrations</h4><p>While I’m prototyping it’s nice to be able to teardown the DB and make it from scratch, handling changes to the data model in the schema. SwiftData on the client lets me do this and I wanted to be able to do it in cloud<sup><a id="footnote-link-2" href="#footnote-2">2</a></sup>.</p><p>And the final goal is:</p><h4>Learn how stuff works</h4><p>I watched <a href="https://developer.apple.com/videos/play/wwdc2023/10166/">the WWDC</a> talks <a href="https://developer.apple.com/videos/play/wwdc2023/10167/">about macros</a> but I had never tried to implement one. Was I scared off by talk of how they increased compilation times? Sure, but as is evident by my embarking on this project, I’ve got plenty of time apparently.</p><h3><code>INSERT INTO blog_posts (title) VALUES ("hello world");</code></h3><p>This whole thing started with <a href="https://github.com/vapor/sql-kit">SQLKit</a>, which seemed to be at a nice level of abstraction for me without pulling too much else in. It’s a nice wrapper around general SQL databases that has adapters for MySQL, Postgres and SQLite that doesn’t try to do too much but handles stuff like:</p><ul><li>DB connections</li><li>Basic querying</li><li>Being able to encode/decode stuff into SQL</li></ul><p>At first I was just doing stuff with SQLKit and tbh, I probably didn’t need to write a wrapper. But where’s the fun in that?</p><h3><code>@Column var usePropertyWrapper = false</code></h3><p>My next step was to figure out how to model models. I wanted to be able to annotate my model’s properties with DB specific stuff like constraints. At first I tried using a <a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/properties/#Property-Wrappers"><code>propertyWrapper</code></a> for this, but I had to bail for a few reasons.</p><p>First, property wrappers don’t know the name of the property they’re wrapping. You can <em>sort of</em> get around this with <code>Mirror</code> trickery, but it felt clunky.</p><p>I want model translation to the DB to be done via <code>Codable</code>. But making a wrapped property <code>Codable</code> means making the property wrapper <code>Codable</code>. That means implementing <code>init(from: any Decoder)</code> and you don’t have access to the normal property wrapper <code>init</code> from there. What am I talking about? Let’s say you’ve got a property wrapper:</p><pre><code><span class="keyword">@propertyWrapper struct</span> ColumnWrapper&lt;Value: <span class="type">ColumnValue</span>&gt; {
  <span class="comment">// We can just store stuff here since we don't actually need to do
  // anything with getting/setting. We just want a place to stash some
  // additional data about a property.</span>
  <span class="keyword">var</span> wrappedValue: <span class="type">Value</span>

  <span class="comment">// This is where we'd store stuff like the raw SQL type, constraints,
  // or a custom column name.</span>
  <span class="keyword">var</span> constraints: [<span class="type">String</span>]

  <span class="keyword">init</span>(constraints: [<span class="type">String</span>] = []) {
    <span class="comment">// Let's just assume we can set a default value so we can have a pretty
    // init that lets us just call @ColumnWrapper(constraints: [...])
    // instead of having to pass a wrappedValue every time.
    //
    // This isn't actually how anything works but just go with me here.</span>
    <span class="keyword">self</span>.<span class="property">wrappedValue</span> = <span class="type">Value</span>.<span class="property">defaultValue</span>
    <span class="keyword">self</span>.<span class="property">constraints</span> = constraints
  }
}
</code></pre><p>And we want to use it like so:</p><pre><code><span class="keyword">struct</span> SomeModel {
  <span class="keyword">@ColumnWrapper</span>(constraints: [<span class="string">"primaryKey"</span>]) <span class="keyword">var</span> id: <span class="type">Int</span>?
}
</code></pre><p>Our <code>ColumnWrapper</code>‘s constraints would then be <code>["primaryKey"]</code>. Awesome. Except when want to make <code>SomeModel</code> decodable, which means that <code>ColumnWrapper</code> needs to be decodable as well. We need to add this to our wrapper to be conformant:</p><pre><code><span class="keyword">init</span>(from decoder: <span class="keyword">any</span> <span class="type">Decoder</span>) <span class="keyword">throws</span> {
  <span class="keyword">self</span>.<span class="property">wrappedValue</span> = <span class="keyword">try</span> decoder.<span class="call">singleValueContainer</span>().<span class="call">decode</span>(<span class="type">Value</span>.<span class="keyword">self</span>)

  <span class="comment">// oh no we can't set constraints here.......</span>
}
</code></pre><p>We could possibly store metadata about properties in some sort of global storage, but that didn’t feel great to me. Neither did switching the model to a class to be able to <a href="https://www.swiftbysundell.com/articles/accessing-a-swift-property-wrappers-enclosing-instance/">access its enclosing instance</a>. And even if it did, I don’t think it’d be great to have every instance have to carry around a definition of its own columns.</p><p>So anyway, property wrappers were out. What next?</p><h2><code>public macro Model() = ?</code></h2><p>I wrote a <code>@Model</code> macro that takes a table name that represents where the model’s records are stored in the DB<sup><a id="footnote-link-3" href="#footnote-3">3</a></sup>. Then it grabs their properties and stores the following:</p><ul><li>The property’s name</li><li>The property’s Swift type (so we can try to figure out how it wants to be stored in the DB)</li><li>Whether or not the property is optional (so we can add <code>NOT NULL</code> constraints in the right places)</li></ul><p>To annotate properties with additional info, I added a <code>@Column</code> wrapper that actually doesn’t expand to anything, it’s just there so that <code>@Model</code> has a place to look for more information like:</p><ul><li>An explicit SQL type that the property wants to be stored as</li><li>Any additional <a href="https://github.com/vapor/sql-kit/blob/main/Sources/SQLKit/Expressions/Clauses/SQLColumnConstraintAlgorithm.swift">constraints supported by SQLKit</a> that should be applied to the property.</li></ul><p>I thought about using a property wrapper instead of a macro that does nothing, but felt like it’s nice that with the macro, the properties are just normal Swift properties, there’s no indirection or <code>Codable</code> trickery required.</p><hr><p>Ok, so now we’ve got a model that looks like this:</p><pre><code><span class="keyword">@Model</span>(table: <span class="string">"people"</span>) <span class="keyword">struct</span> Person {
  <span class="comment">// We assume this is the primary key since it's named `id`
  // so it gets a PRIMARY KEY AUTO_INCREMENT. Could maybe this
  // configurable at some point…</span>
  <span class="keyword">var</span> id: <span class="type">Int</span>?

  <span class="comment">// Adds a `NOT NULL` to the `age` column</span>
  <span class="keyword">var</span> age: <span class="type">Int</span>

  <span class="comment">// Adds a unique index (courtesy of SQLKit)</span>
  <span class="keyword">@Column</span>(.<span class="dotAccess">unique</span>) <span class="keyword">var</span> name: <span class="type">String</span>

  <span class="comment">// We can store this string as a blob for some reason</span>
  <span class="keyword">@Column</span>(type: .<span class="dotAccess">blob</span>) <span class="keyword">var</span> about: <span class="type">String</span>?
}
</code></pre><p>The macro expands into an extension that looks like this:</p><pre><code><span class="keyword">extension</span> <span class="type">Person</span>: <span class="type">StorableModel</span> {
  <span class="keyword">static let</span> _$table = <span class="string">"people"</span>
  <span class="keyword">static var</span> _$columnsByKeyPath: [<span class="type">AnyHashable</span>: <span class="type">ColumnDefinition</span>] {
    [
      \<span class="type">Person</span>.<span class="property">id</span>: <span class="type">ColumnDefinition</span>(name: <span class="string">"id"</span>, sqlType: <span class="keyword">nil</span>, swiftType: <span class="type">Int</span>.<span class="keyword">self</span>, isOptional: <span class="keyword">true</span>, constraints: []),
      \<span class="type">Person</span>.<span class="property">age</span>: <span class="type">ColumnDefinition</span>(name: <span class="string">"age"</span>, sqlType: <span class="keyword">nil</span>, swiftType: <span class="type">Int</span>.<span class="keyword">self</span>, isOptional: <span class="keyword">false</span>, constraints: []),
      \<span class="type">Person</span>.<span class="property">name</span>: <span class="type">ColumnDefinition</span>(name: <span class="string">"name"</span>, sqlType: <span class="keyword">nil</span>, swiftType: <span class="type">String</span>.<span class="keyword">self</span>, isOptional: <span class="keyword">false</span>, constraints: [.<span class="dotAccess">unique</span>]),
      \<span class="type">Person</span>.<span class="property">about</span>: <span class="type">ColumnDefinition</span>(name: <span class="string">"about"</span>, sqlType: .<span class="dotAccess">blob</span>, swiftType: <span class="type">String</span>.<span class="keyword">self</span>, isOptional: <span class="keyword">true</span>, constraints: [])
    ]
  }
}
</code></pre><p>It’s a dictionary where the keys are the key path for the property (this makes it easy to look up information about a column from places we might need it) and the values are <code>ColumnDefinition</code> objects that contain everything that was defined in <code>@Column</code>.</p><p>You might notice that the <code>id</code> column doesn’t have the primary key constraint specified. That’s because I wanted to keep the macro as small as possible, just pulling stuff out of the source. The logic about what SQL types get inferred from Swift types happens elsewhere.</p><hr class="section-break" /><p>Now we’ve got enough information to automatically create database tables. How does that work? Well I feel like this post is already pretty long so I think I’m gonna do that in another one.</p><p>Stay tuned for some of the nuts and bolts of talking to SQLKit and my journey into the center of the predicate.</p><hr><div class="footnote" id="footnote-1">
	<strong class="footnote-number">1.</strong>
	<p>I was a Rails developer for like fifteen years so it probably made sense to use Rails. But I really dig Swift and this project isn’t really <strong>for</strong> anything so I thought I’d give Swift a shot. Swift on the server has a bunch of great stuff these days. <br></br>It’s just fun you know? <a class="back" href="#footnote-link-1"><small>Back to post</small> &hookleftarrow;&#xFE0E;</a></p>
</div><div class="footnote" id="footnote-2">
	<strong class="footnote-number">2.</strong>
	<p>A little beelink server I have running in my office. <a class="back" href="#footnote-link-2"><small>Back to post</small> &hookleftarrow;&#xFE0E;</a></p>
</div><div class="footnote" id="footnote-3">
	<strong class="footnote-number">3.</strong>
	<p>I probably could have derived the table name from the name but I like plural table names and I didn’t want to handle inflection. <a class="back" href="#footnote-link-3"><small>Back to post</small> &hookleftarrow;&#xFE0E;</a></p>
</div>]]></content:encoded><pubDate>Wed, 15 May 2024 00:00:00 +0000</pubDate></item><item><guid>https://patstechweblog.com/posts/2-live-model</guid><title>@LiveModel in SwiftData</title><link>https://patstechweblog.com/posts/2-live-model</link><description>Keep a `SwiftData` model up to date.</description><content:encoded><![CDATA[<p>Ok, so you’ve got a <code>SwiftUI</code> view that displays a <code>SwiftData</code> record. Maybe the model looks like this:</p><pre><code><span class="keyword">@Model final class</span> Person {
  <span class="keyword">var</span> name: <span class="type">String</span>
  <span class="keyword">var</span> friendCount: <span class="type">Int</span> = <span class="number">0</span>

  <span class="keyword">init</span>(name: <span class="type">String</span>) {
    <span class="keyword">self</span>.<span class="property">name</span> = name
  }
}
</code></pre><p>The view maybe looks like this:</p><pre><code><span class="keyword">struct</span> PersonView: <span class="type">View</span> {
  <span class="keyword">var</span> person: <span class="type">Person</span>

  <span class="keyword">var</span> body: <span class="keyword">some</span> <span class="type">View</span> {
    <span class="type">HStack</span> {
      <span class="type">Text</span>(person.<span class="property">name</span>)
      <span class="type">Text</span>(<span class="string">"</span>\(person.<span class="property">friendCount</span>) <span class="string">friends"</span>)
    }
  }
}
</code></pre><p>If updates happen in our container’s <code>mainContext</code>, the view will be updated. But what if an update happens in a background context? Like this?</p><pre><code><span class="type">Task</span> {
  <span class="comment">// Use a background task to update the person's friendCount</span>
  <span class="keyword">let</span> context = <span class="type">ModelContext</span>(container)

  <span class="comment">// Assume we've got a `personID` from somewhere</span>
  <span class="keyword">let</span> person: <span class="type">Person</span> = context.<span class="call">registeredModel</span>(for: personID)!

  <span class="comment">// Update the friend count</span>
  person.<span class="property">friendCount</span> += <span class="number">1</span>

  <span class="comment">// Save the background context</span>
  <span class="keyword">try</span>! context.<span class="call">save</span>()
}
</code></pre><p>The view won’t be updated and we’ll look like fools.</p><p>Unfortunately there’s not really a way (afaik) to be updated when a <strong>Swift</strong>Data store changes, but there <strong>is</strong> a way to do it with <strong>Core</strong>Data. Read <a href="https://fatbobman.com/en/posts/use-core-data-features-in-swiftdata-by-swiftdatakit/">this post for more details</a>. Or check out <a href="https://github.com/fatbobman/SwiftDataKit">fatbobman/SwiftDataKit</a> to be able to hook into what that blog post describes. Or keep reading to see how I’m using it. Or go have a cup of coffee, pet a cat, do whatever you want.</p><h2>The <code>@LiveModel</code> property wrapper</h2><p>Let’s use some stuff from SwiftDataKit to implement a property wrapper that keeps our model up to date, no matter where updates happen from. (This is something <a href="https://github.com/marcoarment/Blackbird/blob/076827d5be06c3a1cf686b2012e8f3853cba7b38/Sources/Blackbird/BlackbirdSwiftUI.swift#L325">marcoarment/Blackbird</a> has and I thought was nice).</p><pre><code><span class="keyword">import</span> Combine
<span class="keyword">import</span> SwiftData
<span class="keyword">import</span> SwiftDataKit
<span class="keyword">import</span> NotificationCenter
<span class="keyword">import</span> CoreData

<span class="comment">// Keep a SwiftData record up to date</span>
<span class="keyword">@propertyWrapper @Observable public final class</span> LiveModel&lt;T: <span class="type">PersistentModel</span>&gt; {
  <span class="keyword">var</span> _model: <span class="type">T</span>

  <span class="keyword">@MainActor public var</span> wrappedValue: <span class="type">T</span> {
    <span class="keyword">get</span> { _model }
    <span class="keyword">set</span> { _model = newValue }
  }

  <span class="keyword">var</span> cancellable: <span class="type">AnyCancellable</span>?

  <span class="keyword">@MainActor public init</span>(wrappedValue: <span class="type">T</span>) {
    <span class="keyword">self</span>.<span class="property">_model</span> = wrappedValue

    <span class="keyword">if let</span> context = wrappedValue.<span class="property">modelContext</span> {
      <span class="keyword">self</span>.<span class="property">cancellable</span> = <span class="type">NotificationCenter</span>.<span class="property">default</span>.<span class="call">publisher</span>(for: .<span class="dotAccess">NSManagedObjectContextDidSave</span>).<span class="property">sink</span> { [<span class="keyword">weak self</span>] notification <span class="keyword">in
        guard let</span> userInfo = notification.<span class="property">userInfo</span>, <span class="keyword">let self else</span> {
          <span class="keyword">return</span>
        }

        <span class="keyword">if let</span> updated = userInfo[<span class="string">"updated"</span>],
           <span class="comment">// Convert to an actual swift set</span>
           <span class="keyword">let</span> set = (updated <span class="keyword">as</span>? <span class="type">NSSet</span> <span class="keyword">as</span>? <span class="type">Set</span>&lt;<span class="type">NSManagedObject</span>&gt;),
           <span class="comment">// See if this update is for our model</span>
           <span class="keyword">let</span> object = set.<span class="call">first</span>(where: { $0.<span class="property">objectID</span>.<span class="property">persistentIdentifier</span> == <span class="keyword">self</span>.<span class="property">_model</span>.<span class="property">id</span> }),
           <span class="comment">// We know we have a persistent identifier because of the above check, so try to reload
           // our model from its context.</span>
           <span class="keyword">let</span> model: <span class="type">T</span> = context.<span class="call">registeredModel</span>(for: object.<span class="property">objectID</span>.<span class="property">persistentIdentifier</span>!)
        {
          <span class="comment">// Update our model, so the Observation system can let the view know.</span>
          <span class="keyword">self</span>.<span class="property">_model</span> = model
        }
      }
    }
  }

  <span class="keyword">deinit</span> {
    cancellable?.<span class="call">cancel</span>()
  }
}
</code></pre><p>Now our model updates whenever it changes, even from a child context. I guess it’d be better if <code>SwiftData</code> did this automatically and maybe the next version will, but for now I’ve found this useful. Maybe you will too? Maybe it’s a terrible idea? Is there some way to do this without reaching into CoreData? Lemme know.</p><p><strong>You can check out a demo of @LiveModel here: <a href="https://github.com/nakajima/LiveModelDemo">https://github.com/nakajima/LiveModelDemo</a>.</strong></p>]]></content:encoded><pubDate>Mon, 6 May 2024 00:00:00 +0000</pubDate></item><item><guid>https://patstechweblog.com/posts/1-some-decodable</guid><title>SomeDecodable</title><link>https://patstechweblog.com/posts/1-some-decodable</link><description>Sometimes you want to parse JSON that doesn’t really agree with Codable.</description><content:encoded><![CDATA[<p>Sometimes you want to parse JSON that doesn’t really agree with Codable’s synthesized init. Like if a key wants to be a single thing <strong>or</strong> an array of that thing. What am I talking about? Ok let’s say you want to parse some JSON into this:</p><pre><code><span class="keyword">struct</span> BlogPost: <span class="type">Codable</span> {
  <span class="keyword">let</span> author: <span class="type">String</span>
}
</code></pre><p>So the JSON you get can look like this:</p><pre><code>{
  <span class="string">"author"</span>: <span class="string">"pat"</span>
}
</code></pre><p>But <em>TWIST</em>, sometimes it gives you this:</p><pre><code>{
  <span class="string">"author"</span>: [<span class="string">"pat"</span>, <span class="string">"evil pat"</span>]
}
</code></pre><p>With <code>Codable</code> that usually means you have to write your own <code>init(from decoder: any Decoder)</code>. And for me that means I usually have to google how to do that again.</p><p>Well now in this very specific case, I don’t anymore because I’m using this:</p><pre><code><span class="keyword">public enum</span> SomeDecodable&lt;T: <span class="type">Decodable</span>&gt;: <span class="type">Decodable</span> {
  <span class="keyword">case</span> none, <span class="call">one</span>(<span class="type">T</span>), <span class="call">many</span>([<span class="type">T</span>])

  <span class="keyword">public init</span>(from decoder: <span class="keyword">any</span> <span class="type">Decoder</span>) <span class="keyword">throws</span> {
    <span class="keyword">let</span> container = <span class="keyword">try</span>! decoder.<span class="call">singleValueContainer</span>()

    <span class="keyword">if let</span> one = <span class="keyword">try</span>? container.<span class="call">decode</span>(<span class="type">T</span>.<span class="keyword">self</span>) {
      <span class="keyword">self</span> = .<span class="call">one</span>(one)
    } <span class="keyword">else if let</span> many = <span class="keyword">try</span>? container.<span class="call">decode</span>([<span class="type">T</span>].<span class="keyword">self</span>) {
      <span class="keyword">self</span> = .<span class="call">many</span>(many)
    } <span class="keyword">else</span> {
      <span class="keyword">self</span> = .<span class="dotAccess">none</span>
    }
  }
}

<span class="comment">// SomeDecodable can have some Equatable, as a treat.</span>
<span class="keyword">extension</span> <span class="type">SomeDecodable</span>: <span class="type">Equatable</span> <span class="keyword">where</span> <span class="type">T</span>: <span class="type">Equatable</span> { }
</code></pre><p>Now, in our model, we can use it:</p><pre><code><span class="keyword">struct</span> BlogPost {
  <span class="keyword">let</span> author: <span class="type">SomeDecodable</span>&lt;<span class="type">String</span>&gt;
}

<span class="keyword">let</span> post = <span class="keyword">try</span> <span class="type">JSONDecoder</span>().<span class="call">decode</span>(<span class="type">BlogPost</span>.<span class="keyword">self</span>, from: json)
<span class="keyword">switch</span> post.<span class="property">author</span> {
<span class="keyword">case let</span> .<span class="call">one</span>(author):
  <span class="call">print</span>(<span class="string">"The author is named</span> \(author)<span class="string">"</span>)
<span class="keyword">case let</span> .<span class="call">many</span>(authors):
  <span class="call">print</span>(<span class="string">"The authors are named</span> \(authors.<span class="call">joined</span>(separator: <span class="string">"</span>, <span class="string">"</span>))<span class="string">"</span>)
<span class="keyword">default</span>:
  <span class="call">print</span>(<span class="string">"I have no idea who the authors are"</span>)
}
</code></pre><p>With <a href="https://www.swift.org/blog/pack-iteration/">parameter pack iteration</a> in Swift 6.0, we might be able to get rid of the enum and support more generic cases, but I haven’t figured out how to do it with Swift 5.10 yet.</p>]]></content:encoded><pubDate>Sat, 4 May 2024 00:00:00 +0000</pubDate></item><item><guid>https://patstechweblog.com/posts/0-git-commit--m-first</guid><title>git commit -m first</title><link>https://patstechweblog.com/posts/0-git-commit--m-first</link><description></description><content:encoded><![CDATA[<p>Hey this is my new blog. Hope you enjoy.</p>]]></content:encoded><pubDate>Fri, 3 May 2024 00:00:00 +0000</pubDate></item></channel></rss>