Raphael Gaschignardhttp://localhost:4000/
Tue, 27 Mar 2018 09:15:48 +0900Tue, 27 Mar 2018 09:15:48 +0900Jekyll v3.7.0Can files co-exist with sandboxing?<p>Remember files? A lot of awesome stuff existed thanks to files being the atom of computation, but it’s all somewhat disappeared of late</p>
<p>So much work on desktops resolves around files. Your desktop has a bunch of files, and you use programs to work on them. So you have your spreadsheets, images, and other goodies. And you have your tools to operate on them.</p>
<p><img src="/files/windows-desktop.png" alt="Serious works" /></p>
<p>so you work off your files. They’re pretty portable on account of being files, and other people can write tools to work on them. So people can write save file editors, data browsers, or even full-fledged clones of other apps.</p>
<hr />
<p>But then Photoshop has access to your <code>.bashrc</code>. The permission model, in practice, is “everything can be touched by anything”. Which is less fun when devices start having GPSes.</p>
<p>So most mobile OSes, realizing that people like security, went to a different model. Each app has its own data stores, and other apps can’t go getting into it.</p>
<p><img src="/files/iphone-desktop.jpg" alt="Security Isolation" /></p>
<p>This new security model is kinda awesome! Your programs work in a mostly isolated environment, so things tend to work by default.</p>
<p>But now when I’m using an application on my phone, by default none of the data is available for transportation. Sure, a lot of bigger apps have Dropbox, but most don’t.</p>
<p>That awesome recipe app with 1000 users but no updates in the past year. It might be storing things in a simple SQLite database, but it doesn’t matter because nothing else will be able to get to the information.</p>
<p>It’s harder to get Libre Office in the universe of mobile apps.</p>
<hr />
<p>A tool-oriented OS is one operating on objects, more than one based in applications(<em>Places</em>). I hope the next generation of OSes will be able to come back to exposing data to the users, instead of sandboxing it along with the apps.</p>
Fri, 19 Jan 2018 00:00:00 +0900http://localhost:4000/2018/01/19/places-and-tools.html
http://localhost:4000/2018/01/19/places-and-tools.htmlThe Two Minute Guide to Simple e-Books<p>Sometimes I’ll find a new website where the content is so entrancing that I want to read through all the archives. But it’s pretty time consuming to sit and read everything at once, and I’ll always lose track of where I am.</p>
<p>Luckily I have a Kindle, a computer, and Python. So I’ve started throwing together simple e-books with this content. I get the nice auto-bookmarking and screen of the Kindle, making for a very nice reading experience.</p>
<p>Here’s the quick step by step guide to packaging up some reading material for those long flights.</p>
<h2 id="find-some-content">Find some content</h2>
<p>The content could be someone’s blog, a couple books from Project Gutenberg, or a batch of e-mails you’ve been meaning to read. Pull in a good amount of content.</p>
<p>I use the EPUB format. At its core, this format is just zipped-up HTML files. So long as your content is simple (X)HTML, then it can be pulled in with little to no modification.</p>
<p>(It’s good form to ask before scraping someone’s website, especially if you aren’t careful with your throttling)</p>
<h2 id="get-that-content">Get that content</h2>
<p>For the most part, I use a small python script with <code>requests</code> to fetch web pages, and save it to disk. Saving the content prevents having to re-fetch if you tweak later steps.</p>
<pre><code>import os
import requests
url = "http://example.com/%s.html"
build_dir = "build/"
if not os.path.exists(build_dir):
os.makedirs(build_dir)
source_urls = [url % i for i in range(500)]
urls = [
(build_dir + "%s.html" % i, url % i) for i in range(500)
]
for filename, url in urls:
print("Getting ", url)
response = requests.get(url)
with open(filename, 'wb') as f:
f.write(response.content)
</code></pre>
<h2 id="format-the-content">Format the content</h2>
<p>the EPUB format works like how you expect it to. Content is broken up into chapters, laid out in a sequential order.</p>
<p>How you want the content to be displayed up to you, but you want the end result to be a collection of HTML pages, representing your chapters.</p>
<p>You can also set some CSS styling, but in my case I don’t bother. The Kindle default text handling is pretty good already.</p>
<p>For web page parsing I opt for <a href="https://pythonhosted.org/pyquery/"><code>pyquery</code></a>.</p>
<pre><code>from pyquery import PyQuery as pq
from ebooklib import epub
def make_chapter(filename):
page = pq(filename=build_dir+filename)
content = page.find('#c1')
title = content.find('h1').text()
date = page.find('.s i').text()
chapter = epub.EpubHtml(
title=date +' : ' + title,
file_name=filename,
lang='en'
)
chapter.content = '&lt;i&gt;' + date +'&lt;/i&gt;' + content.html()
return chapter
</code></pre>
<h2 id="package-up-your-content">Package up your content</h2>
<p>I use the nifty <a href="https://github.com/aerkalov/ebooklib/">ebooklib</a> to package the content up. The example script given in the project is straightforward: give this library a bunch of chapters and it will generate a nice file to hand over.</p>
<pre><code>from ebooklib import epub
book = epub.EpubBook()
...
for chapter in chapters:
book.add_item(chapter)
...
epub.write_epub('output.epub', book, {})
</code></pre>
<h2 id="get-the-file-to-your-reading-device">Get the file to your reading device</h2>
<p>I use <a href="https://calibre-ebook.com/">Calibre</a> or the <a href="">Send to Kindle</a> feature that lets you send files via e-mail.</p>
<p>Do whatever works for you, but the e-mail one is particularly fun when hooked up with automation (get your weekly reports automatically on your device!)</p>
<p>This works well for simple stuff (I’ve never bothered with having working images. for example), but I have an <a href="https://gist.github.com/rtpg/de6bcde42d3628d6c486d7ff7b761b8d">IPython Notebook script</a> that has the full script I use as a template when working on these. Play around with it, and get reading!</p>
Sat, 17 Sep 2016 00:00:00 +0900http://localhost:4000/2016/09/17/make-an-ebook.html
http://localhost:4000/2016/09/17/make-an-ebook.htmlBeing Careful Around ORMs<p>ORMs have been a huge time saver for society. They line up pretty well with the “enterprise”-y software most of us write. They eliminate a good class of potential errors involved in marshalling data through SQL. The amount of things only expressible in SQL has gone <a href="https://docs.djangoproject.com/en/1.10/ref/models/expressions/">way down</a> over the years.</p>
<p>There is a bit of a danger in how ORMs get used. The classy nature of ORMs make us reach for seductively simple OOP tools. But these tools cut us sharply, especially when working in a language without much static checking.</p>
<p>I want to show you a safer way of working with ORMs. Ones that rely on some more basic primitives, but that will avoid many common mistakes that I have experienced in the past with the standard ways.</p>
<p>Let’s say you’re building a simple Django messaging app:</p>
<pre><code>class Message(models.Model):
date = models.DateField(default=datetime.now)
sender = models.ForeignKey(User)
receiver = models.ForeignKey(User)
body = models.TextField()
</code></pre>
<p>So you have this raw model that gives you a nice interface to the database to create messages, and query for messages.</p>
<p>You could write pages to send messages and view them:</p>
<pre><code> def inbox_view(request):
messages = Message.objects.filter(receiver=request.user)
return render( 'inbox.html', { 'messages': messages })
def send_message_view(request, receiver_id):
receiver = User.objects.get(username=receiver_id)
# create the message
Message.objects.create(
sender=request.user,
receiver=receiver,
body=request.POST
)
return redirect(inbox_view)
</code></pre>
<p>You release your app and it mostly works. But then the first feature request comes in: users want to be notified by email when someone sends them a message.</p>
<p>We’ll just add a function call to the controller that sends messages:</p>
<pre><code> def send_message_view(request, receiver_id):
...
send_email(receiver.email, "You have a new message!")
....
</code></pre>
<p>Another feature request comes in, to bulk send messages to many users:</p>
<pre><code> def send_bulk_message_view(request):
...
for receiver in receiver_list:
Message.objects.create(
sender=request.user,
receiver=receiver,
body=message_body
)
</code></pre>
<p>The straightforward way to write this, but we missed a spot! We forgot to add notifications!</p>
<p>Clearly we should be sending notifications no matter what the sending method is.</p>
<p>The usual ORM approach is to override the saving mechanism in your model. No matter where you create your messages, you should send a notification:</p>
<pre><code>class Message(models.Model):
...
def save(self, *args, **kwargs):
super(Message, self).save(*args, **kwargs)
send_email(self.receiver.email, "You have a new message!")
</code></pre>
<p>Awesome, all creations should be caught, and you’ll always send the notification.</p>
<p>You add a feature to edit the messages.</p>
<p>Editing the message calls the model <code>save</code> method, so now a user gets a notification each time the message is edited. Time to fix that as well:</p>
<pre><code> class Message(models.Model):
...
def save(self, *args, **kwargs):
# no primary key yet =&gt; creation, not edit
is_creation = self.pk
super(Message, self).save(*args, **kwargs)
if is_creation:
send_email(self.receiver.email, "You have a new message!")
</code></pre>
<p>You decide to backfill some messages from a legacy system, by creating a bunch of new <code>Message</code>s. Your users get a bunch of spam notifications for messages from 3 years ago. Not super intended.</p>
<p>The problem with overriding is that it doesn’t follow good OOP principles (the “L” in <a href="https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)">SOLID</a>). If some code is working with <code>Model</code>s, and has <code>Message</code>s, it should be able to call <code>save</code> without a bunch of unexpected side-effects. The standard advice of overriding to change behaviour has a high probability of unintended consequences.</p>
<p>Your top-level application code should <em>almost never</em> be directly calling ORM methods. A blunt guideline, but one that pays off quickly.</p>
<hr />
<p>An alternative approach:</p>
<pre><code> def send_message(sender, receiver, message):
return Message.objects.create(sender=sender, receiver=receiver, message=message)
def lookup_user(user_id):
return User.objects.filter(username=user_id)
def send_message_view(request, receiver_id):
receiver = lookup_user(receiver_id)
send_message(request.user, receiver, request.POST)
return redirect(inbox_view)
</code></pre>
<p>Instead of directly accessing the ORM, we write a (thin) layer over it to represent our business logic. This seems obvious, but we’re often tempted to not do so for simple things. But <em>even for simple things</em>, noting the intent is useful, so going the extra mile with these methods is worth it.</p>
<p>When <code>message.save()</code> is run, it’s impossible for the code to know the intent, or the source of the request. But if <code>send_message</code> is called, then it’s much clearer. And, perhaps more importantly, the range of inputs to deal with is much more restricted.</p>
<p>If you go with this, implementing bulk sending and messaging takes little time, and will be right the first time:</p>
<pre><code>def notify_about_message(msg):
# TODO check if user disabled email notifications
send_email(msg.receiver, "You have a message!")
def send_message(sender, receiver, message):
message = Message.objects.create(sender=sender, receiver=receiver, message=message)
notify_about_message(message)
def bulk_send_message_view(request):
...
for receiver in receiver_list:
send_message(request.user, receiver, message_body)
</code></pre>
<p>It feels annoying, because you’re writing a bunch of one-line functions. It feels like over-engineering.</p>
<p>But this is also great documentation. And you’re giving yourself clean entry points for many more features. This model works especially well for the sort of CRUD enterprise apps a lot of us are building.</p>
<p>My recommendation is to fight the urge to not be abstract, and to try to make your business logic layer protect the top layer from the ORM. This logic can also extend to calling other third-party libraries as well. You will end up with an application that is much more refactorable than the alternative.</p>
Mon, 12 Sep 2016 00:00:00 +0900http://localhost:4000/2016/09/12/orms-are-scary.html
http://localhost:4000/2016/09/12/orms-are-scary.htmlJavascript Promises and Causality<p>Let’s say you’re building a standard web application, where you have Clients, and Documents for those clients. You want to build a page that lets you browse documents, based off of clients. Something like the following:</p>
<pre><code> [ Client A | V ] &lt;- Client Selector
---------------------
- Document A &lt;--- List of Client A's documents
- Document B
</code></pre>
<p>You decide to load in the list of documents only after a client is selected, and to do so through an AJAX request.</p>
<p>Your code could end up looking like this (if you’re working in Angular):</p>
<pre><code>$scope.$watch('client', function(){
// when the chosen client changed
// get document list URL for client
var url = documentUrl($scope.client);
$http.get(documentUrl).then(
function(response) {
// after getting the documents, update the document data
$scope.documents = response.data;
// after setting scope value, Angular template
// automatically renders document list
});
});
</code></pre>
<p>Pretty simple, right? You pick a different client, a new request goes out, and when it finishes you set the document list to the new docs.</p>
<p>The problem comes whenever you move a bit too fast. What if the following happened?</p>
<ul>
<li>Pick Client A</li>
<li>HTTP request goes out for documents of Client A (1)</li>
<li>Pick Client B</li>
<li>HTTP request goes out for documents of Client B (2)</li>
<li>Request (2) finishes, setting <code>documents</code> to the documents of Client B</li>
<li>Request (1) finishes, setting <code>documents</code> to the documents of Client A</li>
</ul>
<p>Because of the asynchronous nature of the update of documents, you can actually end up with a pretty nasty race condition! You’ll have Client B picked, but Client A’s documents selected. In itself it’s not too much of an issue, but it does break a pretty fundamental invariant of your program! If you used this data for any subsequent operation, it could lead to a lot of confusion.</p>
<p>If you use promises without thinking, you can quickly run into this situation as well. I’m so used to having causality when writing blocking, synchronous programs that I do not have the right habits when I lose it.</p>
<p>Here are a couple ways we can solve this bug. I do not have a more general solution the problem, so more exploration is needed though.</p>
<h2 id="be-closer-to-the-underlying-data">Be Closer to the Underlying Data</h2>
<p>In the initial implementation our view defined a client and a list of documents. But really, you’re choosing a client and then showing <em>that client’s</em> documents. So one way of tackling this issue is putting the list of documents “inside” the client:</p>
<pre><code>$scope.$watch('client', function(){
// when the chosen client changed
var url = documentUrl($scope.client); // get document list URL for client
$http.get(documentUrl).then(
function(response) {
// after getting the documents
//update the *client's* document data
$scope.client.documents = response.data;
});
});
</code></pre>
<p>Actually, that’s wrong. Because if we do have a race, <code>$scope.client</code> could have changed to the wrong client by the time we treat the response! So what we need to do is capture the current client, and only use that. In Angular watches, those are passed as parameters to the watch function:</p>
<pre><code>$scope.$watch('client', function(newClient){ // &lt;-- reference to the client
// when the chosen client changed
var url = documentUrl(newClient); // get document list URL for client
$http.get(documentUrl).then(
function(response) {
// after getting the documents, update the *client's* document data
// assign to the (unchanging) client
newClient.documents = response.data;
});
});
</code></pre>
<p>In this new model, even if there is a race in the requests, documents to Client B will still receive documents for Client B, and similarly for Client A.</p>
<p>The issue with this solution comes to when you’re doing business logic on your objects elsewhere. if you have a client <code>c</code>, what is <code>c.documents</code>? If you have pagination on your endpoint, is it the “current page”? If you haven’t tried fetching the documents yet, it’s simply <code>undefined</code>. You can build a consistent model, but without using something like TypeScript, it can be easy to have an <code>if(!c.documents)</code> branch that meant to check for lack of documents, but is also catching if you simply haven’t made the document request yet.</p>
<h2 id="short-circuit-your-requests">Short Circuit your Requests</h2>
<p>There are certain sets of requests where you’ll only want the results of at most one outstanding request. For example, once we make a request for the documents of Client B, we know that we no longer need the results of the outstanding request of Client A.</p>
<p>In that case, we could simply ensure that <em>at most one</em> request stays active at a time. If we stop any outstanding request before starting a new one, those requests will fail (and thus not overwrite the documents). So if a request succeeds, you “know” that the documents are for the last chosen client.</p>
<pre><code>var currentRequestCanceller = undefined;
function fetchDocuments(client){
// cancel any ongoing request
if(currentRequestCanceller !== undefined){
currentRequestCanceller.resolve();
// resolving the canceller triggers a timeout in the request
// (see below)
}
// create a new deferred promise to serve as a timeout for the request
currentRequestCanceller = $q.defer();
// start a new request
$http.get(
documentUrl(client),
{timeout: currentRequestCanceller.promise}
// if the timeout resolves, the request is cancelled
).then(function(response){
// only runs if the request wasn't cancelled
$scope.documents = response.data;
});
};
$scope.$watch('client', function(){ fetchDocuments($scope.client)});
</code></pre>
<p>This solution is pretty interesting. It kills unneeded requests, and “debounces” the information requests. The main issue with this solution is that it can be hard to manage if you are chaining requests together. You’re also forced to declare things cleanly, instead of ad-hocing it with a lot of anonymous functions (but that’s more a feature than a bug).</p>
<h2 id="forcing-causality">Forcing Causality</h2>
<p>I’m going to start off by saying that this is not the recommended solution. But it’s the <em>fun</em> solution, and isn’t that what really matters in life?</p>
<p>Here we end up with races because our asynchronous requests can complete in a different order than the request order. So what if we just forced requests to resolve in the “right” order?</p>
<pre><code>var firstRequest = $q.defer(),
requestChain = firstRequest.promise;
firstRequest.resolve(); // kickoff the request chain
function synchronised(nextPromise){
// first, build a promise to track the finalisation of your previous requests
var prevRequestsFinished = $q.defer();
// resolve the request on success or failure
requestChain.then(
function(){ prevRequestsFinished.resolve(); },
function(){ prevRequestsFinished.resolve(); }
);
// update requestChain to a new promise that resolves
// whenever all requests are finished
// (and returns the result from the next promise)
requestChain = $q.all(
[prevRequestsFinished.promise, nextPromise]
).then(
//unwrap the result on success
function(result){ return result[1]; }
);
return requestChain;
}
//...
synchronised($http.get(urlA)).then(
function(response){ console.log("request A finished!");}
);
synchronised($http.get(urlB)).then(
function(response){ console.log("request B finished!");}
);
// "request A finished!" will always show before "request B finshed!"
// even if request A is slower and finishes after request B
</code></pre>
<p>Here, we’ve written a function to “chain” all our promises globally. our <code>synchronised</code> function is actually putting our requests on a queue, where a promise cannot resolve until all previously inserted promises have been resolved.</p>
<p>Even though our HTTP requests are going out in parallel, and might finish out of order, the resolution of the promises will not happen out of order! Our race condition will disappear!</p>
<pre><code>synchronised($http.get(documentUrl)).then(
/* stuff that will now run in the same order as other synchronised calls */
)
</code></pre>
<p>But, of course, if your requests take different amounts of time, you could lose time waiting for requests to resolve. And you generally lose a lot of advantages of the asynchronous model.</p>
<p>Which is why this isn’t recommended! But it’s fun to think of a way to “force” the synchronous model back into an asynchronous-by-default one.</p>
<hr />
<p>I’m not satisfied with any of these solutions, really. The second one is the cleanest, but relies heavily on the single-threaded nature of Javascript to work. I would really like to find a way of thinking that could easily be applied to different execution models.</p>
<p>If you have any alternative solutions that are interesting, please feel free to leave a comment below!</p>
Sat, 06 Aug 2016 00:00:00 +0900http://localhost:4000/2016/08/06/promises-plus-fetching.html
http://localhost:4000/2016/08/06/promises-plus-fetching.htmlSupercharged Types<p>So I’ve been messing with <a href="http://www.purescript.org/">Purescript</a> recently. Purescript is a purely functional language that compiles to Javascript. It includes support for things like object literals that make Javascript interop pain-free. It also has mature tooling, making getting started quite simple.</p>
<p>It also has <em>Row Polymorphism</em>. I don’t want to exagerate but this is a dream come true when it comes to encoding of best practices into types. For all you Node.js servers out there, here’s something to help greatly reduce bugs!</p>
<p>Let’s walk through a tiny example. I’m not going to explain much about the type mechanisms themselves, but will try to give you a taste of what is possible:</p>
<pre><code>foreign import data DB :: !
type User = { username :: String, email :: String, uid :: String}
foreign import createUser :: forall e. User -&gt; Eff (write::DB | e) User
foreign import lookupUser :: forall e. String -&gt; Eff (read::DB | e) (Maybe User)
</code></pre>
<p>(<code>foreign import</code> means the functions are defined in native Javascript, à la <code>extern</code> in C)</p>
<p>In the type of <code>createUser</code> you have <code>forall e. Eff (write::DB | e) User</code>. This is us saying that <code>createUser</code> writes to the DB, and gives us a <code>User</code> . For people with Haskell experience, <code>Eff</code> is kinda like <code>IO</code>, things with side effects end up in <code>Eff</code>.</p>
<p>The type of <code>lookupUser</code> is similar:</p>
<pre><code>forall e. String -&gt; Eff (read::DB | e) (Maybe User)
</code></pre>
<p>Give a <code>String</code> (here, a username), and return an action that will read to the DB and return a <code>User</code> if it was found.</p>
<hr />
<p>Now to put some of this in action. Here’s a simple method that will create a user and then look it up. Maybe you use it for a simple integration test. Maybe you use it as a contrived example:</p>
<pre><code>createThenLookupUser u = do
createdUser &lt;- createUser u
lookupUser createdUser.username
</code></pre>
<p>So the infered type of this is a bit funky because of our <code>User</code> type, but here’s the equivalent type:</p>
<pre><code>createThenLookupUser:: forall e.
User -&gt; Eff (read::DB, write::DB | e) (Maybe User)
</code></pre>
<p>Check that out! The type inferencer figured out that <code>createThenLookupUser</code>:</p>
<ul>
<li>reads the DB (<code>read::DB</code>)</li>
<li>writes to the DB (<code>write::DB</code>)</li>
</ul>
<p>Row polymorphism here is used to encode <em>effects</em>. We can combine actions within <code>Eff</code>, and the type system will accumulate the effects and keep track of them.</p>
<hr />
<p>So now that we have these types, how do they help us?</p>
<p>Now for the good bit. I’m going to write a tiny web app that lets us signup by creating a <code>User</code></p>
<p>First some simple abstractions:</p>
<pre><code>data Method = GET | POST
-- web request
type Request = {
body :: String,
header :: String,
method :: Method
}
-- response
type Response = {
body :: String,
status :: Int
}
</code></pre>
<p>OK, so we’re going to write an endpoint to register to our service! You’ll pass in the username in the header, and your email in the body.</p>
<pre><code>signupPage :: forall e. Request -&gt; Eff (read::DB, write::DB | e) Response
signupPage req = do
mUser &lt;- lookupUser req.header
case mUser of
Just user -&gt; return {
body : "User with this username already exists!",
status : 400
}
Nothing -&gt; do
createUser {
username : req.header,
email: req.body,
uid: "999" -- TODO make more unique
}
return {
body : "Created User with name " ++ req.header,
status: 200
}
</code></pre>
<p>So this signup workflow is pretty straightforward:</p>
<ul>
<li>The type mentions that this function does DB reads and DB writes (you can leave it out and Purescript will infer this)</li>
<li>We first try to lookup if there’s a user with the username already</li>
<li>if so, we return a 400 response (Username already exists)</li>
<li>if not, we create the user and return a 200</li>
</ul>
<p>Awesome. This is the kind of thing people write in the Real World(TM).</p>
<hr />
<p>OK! Let’s write a router!</p>
<p>People always tell me that GETs should be idempotent. Now, this doesn’t strictly mean that GETs can’t commit changes. <em>But</em> if we were to, say, not allow changes on GETs that would get us idempotency. And it’s not bad advice in general.</p>
<pre><code>data Route =
GetRoute String (forall e. Request -&gt; Eff (read::DB | e) Response)
| PostRoute String (forall e. Request -&gt; Eff (read::DB,write::DB | e) Response)
</code></pre>
<p>So here we’ve defined a <code>GetRoute</code> to only accept <code>Eff</code> actions that read our database. However,
<code>PostRoute</code> will accept <code>Eff</code> actions that read or write to our Database.</p>
<p>Let’s write our main routes. Well, our one route, to <code>/signup</code>:</p>
<pre><code>routes :: Array Route
routes = [
GetRoute "/signup" signupPage
]
</code></pre>
<p>So here the only route is making a <code>GET</code> request to <code>/signup</code> will hit the previous signup endpoint.</p>
<p>But if you try to compile this:</p>
<pre><code>Could not match type
( write :: DB, read :: DB | _0)
with type
( read :: DB | e0)
</code></pre>
<p>Uhoh! <code>signupPage</code> totally writes to the DB! Luckily, our restrictions placed on <code>GetRoute</code>
do not let me make my signup endpoint be a <code>GET</code>, and a type error was triggered</p>
<p>If I change my route to use a <code>PostRoute</code>, though, I
would be allowed to write to the DB, because of the types of <code>PostRoute</code> and <code>signupPage</code>:</p>
<pre><code>signupPage :: forall e. Request -&gt; Eff (read::DB, write::DB | e) Response
PostRoute String (forall e. Request -&gt; Eff (read::DB,write::DB | e) Response)
GetRoute String (forall e. Request -&gt; Eff (read::DB | e) Response)
-- GetRoute's 2nd param is missing write::DB
-- so cannot accept actions with writes
</code></pre>
<p>We can then configure our routes and avoid a type error</p>
<pre><code>routes = [
PostRoute "/signup" signupPage
]
</code></pre>
<hr />
<p>So all of this works through Purescript’s row polymorphism and <code>Eff</code>, the extensible effect monad, which is much better explained through the excellent <a href="https://leanpub.com/purescript/read#leanpub-auto-the-eff-monad-1">Purescript By Example</a>.</p>
<p>The ability to track which effects are happening in which functions lets you encode <em>so many</em> best practices.</p>
<p>Some examples:</p>
<ul>
<li>Don’t contact external services inside a request cycle</li>
<li>Don’t make any DB queries that could be unbounded in size inside a request cycle</li>
<li>Make sure requests go through your auth subsystem</li>
<li>Track locks on unique resources</li>
<li>Don’t call functions that are considered long-running unless explicitly acknowledging so</li>
</ul>
<p>Because the universe of statically verifiable constraints gets so much bigger with this, you can venture into API design that might be risky otherwise. And, of course, make your code less buggy in general.</p>
Wed, 20 Jul 2016 00:00:00 +0900http://localhost:4000/2016/07/20/supercharged-types.html
http://localhost:4000/2016/07/20/supercharged-types.htmlWhat's Your Growth?<p>Growth numbers in startups are always pretty deceiving. Here’s a list of things that sound
impressive:</p>
<ul>
<li>Tripled in size in 30 months</li>
<li>Doubling every 18 months</li>
</ul>
<p>Here’s a thing that sounds (slightly) less impressive:</p>
<ul>
<li>55% YoY Growth</li>
<li><em>3%</em> Monthly Growth</li>
</ul>
<p>Of course all of these are equivalent.</p>
<p>Meanwhile 0.2% daily growth will give you 100% Year over Year growth</p>
<p>I wrote a nifty little <a href="http://rtpg.co/whats-your-growth/">calculator</a> to help figure out
quick conversions between monthly growth and metrics like doubling rate, or yearly growth. Check it out!</p>
Wed, 01 Jun 2016 00:00:00 +0900http://localhost:4000/2016/06/01/whats-your-growth.html
http://localhost:4000/2016/06/01/whats-your-growth.html