diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..bb7d3f1 Binary files /dev/null and b/favicon.ico differ diff --git a/img/blog.gif b/img/blog.gif new file mode 100644 index 0000000..643dfb0 Binary files /dev/null and b/img/blog.gif differ diff --git a/img/book-cover.png b/img/book-cover.png new file mode 100644 index 0000000..a913413 Binary files /dev/null and b/img/book-cover.png differ diff --git a/img/haskellers.png b/img/haskellers.png new file mode 100644 index 0000000..1589ea2 Binary files /dev/null and b/img/haskellers.png differ diff --git a/img/luach.png b/img/luach.png new file mode 100644 index 0000000..7e8e86b Binary files /dev/null and b/img/luach.png differ diff --git a/img/orangeroster.png b/img/orangeroster.png new file mode 100644 index 0000000..a043eb3 Binary files /dev/null and b/img/orangeroster.png differ diff --git a/img/photoblog.png b/img/photoblog.png new file mode 100644 index 0000000..c188658 Binary files /dev/null and b/img/photoblog.png differ diff --git a/img/profile.jpg b/img/profile.jpg new file mode 100644 index 0000000..72a97a5 Binary files /dev/null and b/img/profile.jpg differ diff --git a/img/resume.png b/img/resume.png new file mode 100644 index 0000000..06f6782 Binary files /dev/null and b/img/resume.png differ diff --git a/img/yesodicon.png b/img/yesodicon.png new file mode 100644 index 0000000..6bb11cc Binary files /dev/null and b/img/yesodicon.png differ diff --git a/index.hamlet b/index.hamlet new file mode 100644 index 0000000..480361b --- /dev/null +++ b/index.hamlet @@ -0,0 +1,137 @@ +$doctype 5 + + + + + Michael Snoyman's homepage + <link rel=stylesheet href=style.lucius> + <body> + <aside> + <img src=profile.jpg alt="Michael Snoyman" width=150 height=213> + <ul> + <li> + <a href="https://plus.google.com/116553865628071717889/posts">+Michael Snoyman + <li> + <a href="https://twitter.com/snoyberg">@snoyberg + <li> + <a href="http://www.haskellers.com/user/snoyberg">I'm a Haskeller + + <div #twitter> + <a class="twitter-timeline" href="https://twitter.com/snoyberg" data-widget-id="296574273508741122">Tweets by @snoyberg</a> + <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script> + + + <article> + <h1>Michael Snoyman + <h2>About Me + <p .aboutme> + I'm a (mainly) Haskell web developer, and lead developer of the <a href="http://www.yesodweb.com">Yesod Web Framework</a>. + I'm the Lead Software Engineer at <a href="http://fpcomplete.com">FP Complete</a>. + I live in Maalot, Israel with my wife and three kids. + + <h2>Publications + <ul> + <li> + <a href="http://aosabook.org/en/posa/warp.html">Performance of Open Source Applications# + . November 2013 + <li> + <a href="http://www.aosabook.org/en/yesod.html">Architecture of Open Source Applications, Volume 2# + . May 2012 + <li> + <a href="http://shop.oreilly.com/product/0636920023142.do">Developing Web Applications with Haskell and Yesod# + . April 2012, O'Reilly + <li> + <a href="http://steve.vinoski.net/pdf/IC-Warp_a_Haskell_Web_Server.pdf">Warp: A Haskell Web Server# + . June 2011, IEEE Spectrum + <i>The Functional Web + + <h2>Talks + <table> + <thead> + <tr> + <th>Date + <th>Title + <th>Venue + <th>Links + <tbody> + <tr> + <td>October 2014 + <td>Hybrid server/client Haskell web apps + <td> + <a href=http://polyconf.com/>PolyConf 2014 + <td>To be posted after the talk + <tr> + <td>October 2014 + <td>Conduit + <td>Haskell Users Group, Berlin + <td>To be posted after the talk + <tr> + <td>February 2014 + <td>Pipes and Conduit + <td>Haskell Cast + <td> + <a href="http://www.haskellcast.com/episode/006-gabriel-gonzalez-and-michael-snoyman-on-pipes-and-conduit/">Video + <tr> + <td>November 2013 + <td>Yesod and FP Complete + <td>Mostly Erlang (podcast) + <td> + <a href="http://mostlyerlang.com/2013/11/27/023-yesod-and-fp-complete/">Audio + <tr> + <td>October 2013 + <td>Programming with Functional Style + <td>Erlang Factory, Tel Aviv + <td> + <a href="https://docs.google.com/presentation/d/1770ZKnm_bUpkOfzYN_IZq7AVFE6J1pKyFMX9iqVgyAI/edit?usp=sharing">Slides + <tr> + <td>Dec 2012 + <td>Haskell and Yesod + <td>Sayeret Lambda + <td> + <a href="https://docs.google.com/presentation/d/1_A1QMbFPmpfBc-uJz8Z4G4tESVuxErW4kmI-R2yVnU4/edit">Slides + <tr> + <td>Dec 2012 + <td>Yesod + <td>Floss Weekly + <td> + <a href="http://twit.tv/show/floss-weekly/236">Video + <tr> + <td>Nov 2012 + <td>Designing Domain Specific Languages + <td>O'Reilly Webinar + <td> + <a href="http://www.oreillynet.com/pub/e/2400">Video + <a href="https://docs.google.com/presentation/d/1flkeCW-8rNPrT1d-YkxMm4repuAeT1YxjmdRd2f-FDs/edit">Slides + <tr> + <td>Oct 2012 + <td>Yesod + <td>Sayeret Lambda (podcast) + <td> + <a href="http://narkisr.github.io/lambda-pod/posts/2012-10/lambda-pod-yesod-episode.html">Audio + <tr> + <td>June 2012 + <td>Designing Type-Safe Haskell APIs + <td>Big Techday 5 + <td> + <a href="http://www.oreillynet.com/pub/e/2351">Video + <a href="https://docs.google.com/presentation/d/1K7smIeqmca-fY8qgQUKrvWWHTUPJoKuVLRtLeWYFMtI/edit">Slides + <tr> + <td>Nov 2011 + <td>Yesod Web Framework + <td>QCon San Francisco + <td> + <a href="http://www.infoq.com/presentations/Yesod;jsessionid=6AF0AA3EF8CCCE7195BDBBB0C8E1050B">Video + <a href="https://docs.google.com/presentation/d/15wN2uLhMdTWqvYD2RtN0W3XFyTYQ0Kh6bHOKlthBhX0/edit">Slides + + <h2>My Sites + <ul> + <li> + <a href="http://photos.snoyman.com">Family Photo Blog + <li> + <a href="http://www.yesodweb.com">Yesod Web Framework + <li> + <a href="http://www.haskellers.com">Haskellers + <li> + <a href="http://packdeps.haskellers.com">Packdeps + <li> + <a href="http://luach.snoyman.com">Luach diff --git a/posts.yaml b/posts.yaml new file mode 100644 index 0000000..80a8c12 --- /dev/null +++ b/posts.yaml @@ -0,0 +1,12 @@ +- file: posts/xslt-rant-explained + title: "XSLT Rant Explained" + day: 2012-04-09 +- file: posts/xslt-rant + title: "Open Letter to XSLT Fans" + day: 2012-04-05 +- file: posts/ie-mimetype-png + title: "Dysfunctional Programming: FindMimeFromData" + day: 2012-03-22 +- file: posts/first-post + title: First Post + day: 2012-01-31 diff --git a/posts/first-post b/posts/first-post new file mode 100644 index 0000000..1d48c03 --- /dev/null +++ b/posts/first-post @@ -0,0 +1 @@ +Most of my blogging still occurs on the [Yesod blog](http://www.yesodweb.com/blog). This blog will be home for posts that are not necessarily Yesod or Haskell related. diff --git a/posts/ie-mimetype-png b/posts/ie-mimetype-png new file mode 100644 index 0000000..c2c3e0b --- /dev/null +++ b/posts/ie-mimetype-png @@ -0,0 +1,76 @@ +I've been a bit torn about what exactly to put on this blog. Short comments go +to Google+. Anything Haskell related goes to the [Yesod blog](http://www.yesodweb.com/blog). +So what goes here? + +For now, I've decided to start a series on dysfunctional programming. In other +words, horror stories from the non-Haskell world. + +Today's tale begins with a strange bug report. One of our clients says that all +of the CSS changes we put on their server have suddenly disappeared. Which is +especially strange, because I don't have any write access to their production +server. There were two aspects to this report: bullets were missing, and the +title image didn't display in IE. By the way, there's a separate title image, +provided by the client, for each publication. + +Forget about the bullets for now, that one wasn't interesting. The title image +was truly a conundrum. I started off by loading the output in Chrome. No +problem. Internet Explorer? Red X. At first I thought it was a URL mangling +issue. But when I tried moving the title image to a simple path on the system +and loading it up, it *still* displayed as a red X. + +I started a local Warp server and accessed the site from there. No problem. + +So... the file itself is correct, and the name of the file isn't the problem. +What's the issue? I spent a good 30 minutes looking for PNG bugs in IE. +Unfortunately, with IE's sordid history of PNG bugs, it was like looking for a +needle in a haystack. However, I saw one off-hand comment: + +> Try right-clicking the image, choosing properties, and look at the type. + +I tried it and, sure enough, the type was "Unknown," not "PNG". So it turns out +that when served from a web server, IE was delivered the correct mimetype via a +response header. But when opening from the local filesystem, it needs to detect +the mimetype automatically. + +Looking at a file extension is too simple. IE needs to outsmart us. The +solution? +[FindMimeFromData](http://msdn.microsoft.com/en-us/library/ie/ms775147%28v=vs.85%29.aspx). +This wonderfully constructed function inspects the contents of the file in +question and determines the mimetype. For example, this nifty little program +will print the mimetype of the title.png file: + + #include <urlmon.h> + #include <stdio.h> + + int main(int argc, char* argv[]) + { + char buff[256]; + LPWSTR out; + + FILE *in = fopen("title.png", "rb"); + + fread(buff, 1, 256, in); + + FindMimeFromData(NULL, NULL, buff, 256, NULL, FMFD_DEFAULT, &out, 0); + + printf("%ls\n", out); + + return 0; + } + +The result? `image/pjpeg` of course! Never mind that that's an invalid mimetype, but the Win32 function is giving the __wrong mimetype for a PNG file__!!! I'm not quite certain how Microsoft screwed this up so royally. There's a very explicit set of bytes at the beginning of the file indicating that it's a PNG, and [imagesize-conduit](http://hackage.haskell.org/package/imagesize-conduit) detects it just fine: + + import Data.Conduit + import Data.Conduit.Binary + import Data.Conduit.ImageSize + + main :: IO () + main = do + f <- runResourceT $ sourceFile "title.png" $$ sinkImageInfo + print f + +In other words: Internet Explorer, since version 4, can't even display some +simple images. + +I had to tell the client that we have no workaround, and they need to either +"fix" their perfectly valid PNG or always serve their content from a webserver. diff --git a/posts/xslt-rant b/posts/xslt-rant new file mode 100644 index 0000000..df1c9c3 --- /dev/null +++ b/posts/xslt-rant @@ -0,0 +1,14 @@ +Dear XSLT fans, + +Please don't insist on calling your bastard child of a language "functional." Just because it got one thing sort-of right (immutable data), doesn't mean that it in any way represents a real functional language. + +I'm not even talking about the hideously verbose syntax, or the completely obtuse data model. The fact that you can't know what any single line of code does without reviewing __every other line in the program__ makes this language an abomination. + +Don't believe me? Ask me for examples, I'll prove it. But anyone who's used XSLT for multi-file projects likely knows I'm right. + +Oh, and the fact that you can call a language functional when it *lacks first class functions* makes my eye twitch. I'm tempted to upload a video of my eye twitching just to prove it. + +Sincerely, +A Haskell programmer who spends an inordinate amount of time debugging bad XSLT. + +PS: I would gladly write a program in Java, or C++, or likely assembly, over XSLT any day. Those languages may have issues, but they're sane tools. XSLT is just fundamentally broken. diff --git a/posts/xslt-rant-explained b/posts/xslt-rant-explained new file mode 100644 index 0000000..3d7a8e0 --- /dev/null +++ b/posts/xslt-rant-explained @@ -0,0 +1,93 @@ +My previous blog post got a bit more attention than I'd anticipated. I hadn't +really intended it as more than a place to link to and let the XSLT people I +work with know that the language isn't representative of functional languages. +However, a number of people have asked for more details. That's the purpose of +this post. + +Before getting into the claims of my post itself, let me address some of the +counter-claims I saw in some of the discussions: + +* <b>XSLT isn't *functional*, it's *declarative*.</b> I can agree with this distinction, and think the XSLT world needs to accept it. +* __XSLT is actually very concise.__ No, it's not. XPath- the query language used by XSLT- is incredibly concise, and I have no issue with it. In fact, when designing [xml-conduit](http://hackage.haskell.org/package/xml-conduit), Aristid and I designed the combinators after XPath. +* __XSLT 2.0 fixes a lot of this stuff.__ XSLT 2.0 is just lipstick on a hog. It changes none of the underlying problems. + +One last bit of explanation: I don't think most people realize the level to +which XSLT is used in some projects. When used as a client-side technology to +convert some simple XML into simple HTML, XSLT can work just fine. I *still* +think it's a horrible language, but it's passable. The real problem is that +*XSLT doesn't scale*. Here's the issue I was alluding to in my previous blog +post. The DITA-OT has some code that looks like: + + + <!-- Copy @id attributes verbatim --> + <xsl:template match="@id"> + <xsl:attribute name="id" select="."/> + </xsl:template> + + <xsl:template match="some-element"> + <xsl:apply-templates select="@id"/> <!-- Applies the default template to the ID attribute --> + <!-- A bunch more ugly code --> + </xsl:template> + +Then, one of my coworkers unwittingly added: + + <!-- Some special case requires printing out the attributes verbatim. --> + <xsl:template match="@*"> + <xsl:if test="$some-special-case"> + <xsl:value-of select="."/> + </xsl:if> + </xsl:template> + +That second block says to print out the raw value of the attribute in question. +The problem is, it overrides the definition of the @id template in the first +block, and now all the ids for some-element are being printed out verbatim, +which is *not* what we wanted! + +So which of those code blocks is wrong? Both of them. You shouldn't be +polluting the global namespace with these kinds of specific templates, they +each should have been put in their own `mode`. But I have two points here: + +* `XSLT` is encouraging people to write bad code by using a global namespace by default. +* It's horribly difficult to find a bug like this. This project is well over 8000 lines of XSLT code, spread across some 40 files. It took me an hour to debug this. Sure, the people writing the original code wrote it badly, but a language shouldn't punish maintainers like this! + +The issue here is one that many XSLT proponents contend is a strength: you can +go ahead and modify the behavior of existing templates in a later file. This +can actually be very convenient. Imagine you're converting DocBook to HTML, and +you have: + + <xsl:template match="para"> + <p> + <xsl:apply-templates/> + </p> + </xsl:template> + +And now you decide that you would like to change this so that a `para` tag is +always given a `class` of `paragraph` in the HTML. You can go ahead and write a +customization: + + <xsl:template match="para"> + <p class="paragraph"> + <xsl:apply-templates/> + </p> + </xsl:template> + +And all of the existing code will automatically use this new template. I won't +argue that this isn't convenient. It certainly is. But it flies in the face of +all good engineering practice. Suddenly, I have no idea what an +`apply-templates` will do. I describe this as: + +> You have no idea what a single line of code will do without analyzing every +> other line of code in your program. + +There are plenty of ways to do this properly in real programming languages. In +my Haskell-based DITA processing code, for example, there is a setting allowing +you to specify specific handling for individual elements. Then in the calling +code, you are explicitly calling into a function for which you don't know what +the output will necessarily be. Everything is properly namespaced and +segregated, and you can know by looking at the code in front of you just how +it's going to be dispatched. + +I'm out of time for now, but if anyone wants to see examples of XML processing +done right, let me know. I will say that for some of my company's newer +products, I've completely reimplemented DITA-to-HTML transforms, and it's +likely a tenth of the size of the DITA-OT's HTML transforms. diff --git a/README b/robots.txt similarity index 100% rename from README rename to robots.txt diff --git a/static/normalize.css b/static/normalize.css new file mode 100644 index 0000000..0e289ab --- /dev/null +++ b/static/normalize.css @@ -0,0 +1,437 @@ +/*! normalize.css 2012-01-22T23:30 UTC - http://github.com/necolas/normalize.css */ + +/* ============================================================================= + HTML5 display definitions + ========================================================================== */ + +/* + * Corrects block display not defined in IE6/7/8/9 & FF3 + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +/* + * Corrects inline-block display not defined in IE6/7/8/9 & FF3 + */ + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +/* + * Prevents modern browsers from displaying 'audio' without controls + */ + +audio:not([controls]) { + display: none; +} + +/* + * Addresses styling for 'hidden' attribute not present in IE7/8/9, FF3, S4 + * Known issue: no IE6 support + */ + +[hidden] { + display: none; +} + + +/* ============================================================================= + Base + ========================================================================== */ + +/* + * 1. Corrects text resizing oddly in IE6/7 when body font-size is set using em units + * http://clagnut.com/blog/348/#c790 + * 2. Prevents iOS text size adjust after orientation change, without disabling user zoom + * www.456bereastreet.com/archive/201012/controlling_text_size_in_safari_for_ios_without_disabling_user_zoom/ + */ + +html { + font-size: 100%; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ + -ms-text-size-adjust: 100%; /* 2 */ +} + +/* + * Addresses font-family inconsistency between 'textarea' and other form elements. + */ + +html, +button, +input, +select, +textarea { + font-family: sans-serif; +} + +/* + * Addresses margins handled incorrectly in IE6/7 + */ + +body { + margin: 0; +} + + +/* ============================================================================= + Links + ========================================================================== */ + +/* + * Addresses outline displayed oddly in Chrome + */ + +a:focus { + outline: thin dotted; +} + +/* + * Improves readability when focused and also mouse hovered in all browsers + * people.opera.com/patrickl/experiments/keyboard/test + */ + +a:hover, +a:active { + outline: 0; +} + + +/* ============================================================================= + Typography + ========================================================================== */ + +/* + * Neutralise smaller font-size in 'section' and 'article' in FF4+, Chrome, S5 + */ + +h1 { + font-size: 2em; +} + +/* + * Addresses styling not present in IE7/8/9, S5, Chrome + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/* + * Addresses style set to 'bolder' in FF3+, S4/5, Chrome +*/ + +b, +strong { + font-weight: bold; +} + +blockquote { + margin: 1em 40px; +} + +/* + * Addresses styling not present in S5, Chrome + */ + +dfn { + font-style: italic; +} + +/* + * Addresses styling not present in IE6/7/8/9 + */ + +mark { + background: #ff0; + color: #000; +} + +/* + * Corrects font family set oddly in IE6, S4/5, Chrome + * en.wikipedia.org/wiki/User:Davidgothberg/Test59 + */ + +pre, +code, +kbd, +samp { + font-family: monospace, serif; + _font-family: 'courier new', monospace; + font-size: 1em; +} + +/* + * Improves readability of pre-formatted text in all browsers + */ + +pre { + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +/* + * 1. Addresses CSS quotes not supported in IE6/7 + * 2. Addresses quote property not supported in S4 + */ + +/* 1 */ + +q { + quotes: none; +} + +/* 2 */ + +q:before, +q:after { + content: ''; + content: none; +} + +small { + font-size: 75%; +} + +/* + * Prevents sub and sup affecting line-height in all browsers + * gist.github.com/413930 + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + + +/* ============================================================================= + Lists + ========================================================================== */ + +ul, +ol { + margin-left: 0; + padding: 0 0 0 40px; +} + +dd { + margin: 0 0 0 40px; +} + +nav ul, +nav ol { + list-style: none; + list-style-image: none; +} + + +/* ============================================================================= + Embedded content + ========================================================================== */ + +/* + * 1. Removes border when inside 'a' element in IE6/7/8/9, FF3 + * 2. Improves image quality when scaled in IE7 + * code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/ + */ + +img { + border: 0; /* 1 */ + -ms-interpolation-mode: bicubic; /* 2 */ +} + +/* + * Corrects overflow displayed oddly in IE9 + */ + +svg:not(:root) { + overflow: hidden; +} + + +/* ============================================================================= + Figures + ========================================================================== */ + +/* + * Addresses margin not present in IE6/7/8/9, S5, O11 + */ + +figure { + margin: 0; +} + + +/* ============================================================================= + Forms + ========================================================================== */ + +/* + * Corrects margin displayed oddly in IE6/7 + */ + +form { + margin: 0; +} + +/* + * Define consistent border, margin, and padding + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/* + * 1. Corrects color not being inherited in IE6/7/8/9 + * 2. Corrects text not wrapping in FF3 + * 3. Corrects alignment displayed oddly in IE6/7 + */ + +legend { + border: 0; /* 1 */ + padding: 0; + white-space: normal; /* 2 */ + *margin-left: -7px; /* 3 */ +} + +/* + * 1. Corrects font size not being inherited in all browsers + * 2. Addresses margins set differently in IE6/7, FF3+, S5, Chrome + * 3. Improves appearance and consistency in all browsers + */ + +button, +input, +select, +textarea { + font-size: 100%; /* 1 */ + margin: 0; /* 2 */ + vertical-align: baseline; /* 3 */ + *vertical-align: middle; /* 3 */ +} + +/* + * Addresses FF3/4 setting line-height on 'input' using !important in the UA stylesheet + */ + +button, +input { + line-height: normal; /* 1 */ +} + +/* + * 1. Improves usability and consistency of cursor style between image-type 'input' and others + * 2. Corrects inability to style clickable 'input' types in iOS + * 3. Removes inner spacing in IE7 without affecting normal text inputs + * Known issue: inner spacing remains in IE6 + */ + +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; /* 1 */ + -webkit-appearance: button; /* 2 */ + *overflow: visible; /* 3 */ +} + +/* + * 1. Addresses box sizing set to content-box in IE8/9 + * 2. Removes excess padding in IE8/9 + * 3. Removes excess padding in IE7 + Known issue: excess padding remains in IE6 + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ + *height: 13px; /* 3 */ + *width: 13px; /* 3 */ +} + +/* + * 1. Addresses appearance set to searchfield in S5, Chrome + * 2. Addresses box-sizing set to border-box in S5, Chrome (include -moz to future-proof) + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/* + * Removes inner padding and search cancel button in S5, Chrome on OS X + */ + +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +/* + * Removes inner padding and border in FF3+ + * www.sitepen.com/blog/2008/05/14/the-devils-in-the-details-fixing-dojos-toolbar-buttons/ + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/* + * 1. Removes default vertical scrollbar in IE6/7/8/9 + * 2. Improves readability and alignment in all browsers + */ + +textarea { + overflow: auto; /* 1 */ + vertical-align: top; /* 2 */ +} + + +/* ============================================================================= + Tables + ========================================================================== */ + +/* + * Remove most spacing between table cells + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/static/style.lucius b/static/style.lucius new file mode 100644 index 0000000..aa68bc8 --- /dev/null +++ b/static/style.lucius @@ -0,0 +1,98 @@ +@headercolor: #000; + +html { + background-color: #ccf; +} +body { + font-family: Calibri, Helvetica, "Droid Sans", "DejaVu Sans", Verdana, sans-serif; + width: 940px; + margin: 1em auto; + padding: 1em; + background-color: #fff; + border: 1px solid #333; +} +header h1 { + margin-top: 0; + font-size: 1em; + font-weight: normal; + color: #{headercolor}; +} +header { + border-bottom: 3px solid #000; + margin-bottom: 2em; +} +.sidebar h1 { + color: #{headercolor}; +} +article { + width: 600px; + float: left; +} +article hgroup { + margin-bottom: 2em; + h1 { + margin: 0; + } + h2 { + font-size: 1em; + font-weight: normal; + font-style: italic; + margin: 0; + color: #666; + } +} +.sidebar { + width: 250px; + float: right; + font-size: 0.8em; +} +nav ul { + padding: 0; +} +.me p { + font-style: italic; +} +.sidebar > * { + border: 1px solid #333; + border-radius: 5px; + padding: 5px; + margin-bottom: 2em; + background-color: #fff8dd; +} +.sidebar h1 { + margin: 0; +} +.links li { + margin-bottom: 0.5em; +} +a, a:visited { + color: #00f; + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +.clearfix { + clear: both; +} +.book img { + width: 150px; +} +.archive section h1 { + font-size: 1em; + color: #000; + margin-top: 1em; +} + +@media (max-width: 960px) { + body { + width: auto; + margin: 0; + } + article, .sidebar { + float: none; + } + article { +width: auto; +} +} diff --git a/style.lucius b/style.lucius new file mode 100644 index 0000000..1e1b088 --- /dev/null +++ b/style.lucius @@ -0,0 +1,61 @@ +html, body { + margin: 0; + padding: 0; + font-family: sans-serif; +} + +aside { + float: right; + width: 300px; +} + +article { + margin-right: 320px; +} + +body { + width: 900px; + margin: 0 auto; +} + +p.aboutme { + white-space: pre-wrap; +} + +table { + border-spacing: 0; + border-collapse: collapse; +} + +tr:nth-child(odd) td { + background-color: #f8f8f8; +} + +td { + font-size: 0.9em; + padding: 2px; +} + +td:nth-child(2) { + font-weight: bold; +} + +@media screen and (max-width: 900px) { + aside { + float: none; + width: auto; + } + + #twitter { + display: none; + } + + body { + width: auto; + margin: 5px; + } + + article { + margin: 0; + } +} diff --git a/torah/CFInstall.js b/torah/CFInstall.js new file mode 100644 index 0000000..66342c8 --- /dev/null +++ b/torah/CFInstall.js @@ -0,0 +1,348 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview CFInstall.js provides a set of utilities for managing + * the Chrome Frame detection and installation process. + * @author slightlyoff@google.com (Alex Russell) + */ + +(function(scope) { + // bail if we'd be over-writing an existing CFInstall object + if (scope['CFInstall']) { + return; + } + + /** + * returns an item based on DOM ID. Optionally a document may be provided to + * specify the scope to search in. If a node is passed, it's returned as-is. + * @param {string|Node} id The ID of the node to be located or a node + * @param {Node} doc Optional A document to search for id. + * @return {Node} + */ + var byId = function(id, doc) { + return (typeof id == 'string') ? (doc || document).getElementById(id) : id; + }; + + ///////////////////////////////////////////////////////////////////////////// + // Plugin Detection + ///////////////////////////////////////////////////////////////////////////// + + /** + * Checks to find out if ChromeFrame is available as a plugin + * @return {Boolean} + */ + var isAvailable = function() { + // For testing purposes. + if (scope.CFInstall._force) { + return scope.CFInstall._forceValue; + } + + // Look for CF in the User Agent before trying more expensive checks + var ua = navigator.userAgent.toLowerCase(); + if (ua.indexOf("chromeframe") >= 0) { + return true; + } + + if (typeof window['ActiveXObject'] != 'undefined') { + try { + var obj = new ActiveXObject('ChromeTab.ChromeFrame'); + if (obj) { + return true; + } + } catch(e) { + // squelch + } + } + return false; + }; + + /** + * Creates a style sheet in the document containing the passed rules. + */ + var injectStyleSheet = function(rules) { + try { + var ss = document.createElement('style'); + ss.setAttribute('type', 'text/css'); + if (ss.styleSheet) { + ss.styleSheet.cssText = rules; + } else { + ss.appendChild(document.createTextNode(rules)); + } + var h = document.getElementsByTagName('head')[0]; + var firstChild = h.firstChild; + h.insertBefore(ss, firstChild); + } catch (e) { + // squelch + } + }; + + /** @type {boolean} */ + var cfStyleTagInjected = false; + /** @type {boolean} */ + var cfHiddenInjected = false; + + /** + * Injects style rules into the document to handle formatting of Chrome Frame + * prompt. Multiple calls have no effect. + */ + var injectCFStyleTag = function() { + if (cfStyleTagInjected) { + // Once and only once + return; + } + var rules = '.chromeFrameInstallDefaultStyle {' + + 'width: 800px;' + + 'height: 600px;' + + 'position: absolute;' + + 'left: 50%;' + + 'top: 50%;' + + 'margin-left: -400px;' + + 'margin-top: -300px;' + + '}' + + '.chromeFrameOverlayContent {' + + 'position: absolute;' + + 'margin-left: -400px;' + + 'margin-top: -300px;' + + 'left: 50%;' + + 'top: 50%;' + + 'border: 1px solid #93B4D9;' + + 'background-color: white;' + + '}' + + '.chromeFrameOverlayContent iframe {' + + 'width: 800px;' + + 'height: 600px;' + + 'border: none;' + + '}' + + '.chromeFrameOverlayCloseBar {' + + 'height: 1em;' + + 'text-align: right;' + + 'background-color: #CADEF4;' + + '}' + + '.chromeFrameOverlayUnderlay {' + + 'position: absolute;' + + 'width: 100%;' + + 'height: 100%;' + + 'background-color: white;' + + 'opacity: 0.5;' + + '-moz-opacity: 0.5;' + + '-webkit-opacity: 0.5;' + + '-ms-filter: ' + + '"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";' + + 'filter: alpha(opacity=50);' + + '}'; + injectStyleSheet(rules); + cfStyleTagInjected = true; + }; + + /** + * Injects style rules to hide the overlay version of the GCF prompt. + * Multiple calls have no effect. + */ + var closeOverlay = function() { + // IE has a limit to the # of <style> tags allowed, so we avoid + // tempting the fates. + if (cfHiddenInjected) { + return; + } + var rules = '.chromeFrameOverlayContent { display: none; }' + + '.chromeFrameOverlayUnderlay { display: none; }'; + injectStyleSheet(rules); + // Hide the dialog for a year (or until cookies are deleted). + var age = 365 * 24 * 60 * 60 * 1000; + document.cookie = "disableGCFCheck=1;path=/;max-age="+age; + cfHiddenInjected = true; + }; + + /** + * Plucks properties from the passed arguments and sets them on the passed + * DOM node + * @param {Node} node The node to set properties on + * @param {Object} args A map of user-specified properties to set + */ + var setProperties = function(node, args) { + + var srcNode = byId(args['node']); + + node.id = args['id'] || (srcNode ? srcNode['id'] || getUid(srcNode) : ''); + + // TODO(slightlyoff): Opera compat? need to test there + var cssText = args['cssText'] || ''; + node.style.cssText = ' ' + cssText; + + var classText = args['className'] || ''; + node.className = classText; + + // default if the browser doesn't so we don't show sad-tab + var src = args['src'] || 'about:blank'; + + node.src = src; + + if (srcNode) { + srcNode.parentNode.replaceChild(node, srcNode); + } + }; + + /** + * Creates an iframe. + * @param {Object} args A bag of configuration properties, including values + * like 'node', 'cssText', 'className', 'id', 'src', etc. + * @return {Node} + */ + var makeIframe = function(args) { + var el = document.createElement('iframe'); + el.setAttribute('frameborder', '0'); + el.setAttribute('border', '0'); + setProperties(el, args); + return el; + }; + + /** + * Adds an unadorned iframe into the page, taking arguments to customize it. + * @param {Object} args A map of user-specified properties to set + */ + var makeInlinePrompt = function(args) { + args.className = 'chromeFrameInstallDefaultStyle ' + + (args.className || ''); + var ifr = makeIframe(args); + // TODO(slightlyoff): handle placement more elegantly! + if (!ifr.parentNode) { + var firstChild = document.body.firstChild; + document.body.insertBefore(ifr, firstChild); + } + }; + + /** + * Adds a styled, closable iframe into the page with a background that + * emulates a modal dialog. + * @param {Object} args A map of user-specified properties to set + */ + var makeOverlayPrompt = function(args) { + if (byId('chromeFrameOverlayContent')) { + return; // Was previously created. Bail. + } + + var n = document.createElement('span'); + n.innerHTML = '<div class="chromeFrameOverlayUnderlay"></div>' + + '<table class="chromeFrameOverlayContent"' + + 'id="chromeFrameOverlayContent"' + + 'cellpadding="0" cellspacing="0">' + + '<tr class="chromeFrameOverlayCloseBar">' + + '<td>' + + // TODO(slightlyoff): i18n + '<button id="chromeFrameCloseButton">close</button>' + + '</td>' + + '</tr>' + + '<tr>' + + '<td id="chromeFrameIframeHolder"></td>' + + '</tr>' + + '</table>'; + + document.body.appendChild(n); + var ifr = makeIframe(args); + byId('chromeFrameIframeHolder').appendChild(ifr); + byId('chromeFrameCloseButton').onclick = closeOverlay; + }; + + var CFInstall = {}; + + /** + * Checks to see if Chrome Frame is available, if not, prompts the user to + * install. Once installation is begun, a background timer starts, + * checkinging for a successful install every 2 seconds. Upon detection of + * successful installation, the current page is reloaded, or if a + * 'destination' parameter is passed, the page navigates there instead. + * @param {Object} args A bag of configuration properties. Respected + * properties are: 'mode', 'url', 'destination', 'node', 'onmissing', + * 'preventPrompt', 'oninstall', 'preventInstallDetection', 'cssText', and + * 'className'. + * @public + */ + CFInstall.check = function(args) { + args = args || {}; + + // We currently only support CF in IE + // TODO(slightlyoff): Update this should we support other browsers! + var ua = navigator.userAgent; + var ieRe = /MSIE \S+; Windows NT/; + var bail = false; + if (ieRe.test(ua)) { + // We also only support Win2003/XPSP2 or better. See: + // http://msdn.microsoft.com/en-us/library/ms537503%28VS.85%29.aspx + if (parseFloat(ua.split(ieRe)[1]) < 6 && + ua.indexOf('SV1') < 0) { + bail = true; + } + } else { + bail = true; + } + if (bail) { + return; + } + + // Inject the default styles + injectCFStyleTag(); + + if (document.cookie.indexOf("disableGCFCheck=1") >=0) { + // If we're supposed to hide the overlay prompt, add the rules to do it. + closeOverlay(); + } + + // When loaded in an alternate protocol (e.g., "file:"), still call out to + // the right location. + var currentProtocol = document.location.protocol; + var protocol = (currentProtocol == 'https:') ? 'https:' : 'http:'; + // TODO(slightlyoff): Update this URL when a mini-installer page is + // available. + var installUrl = protocol + '//www.google.com/chromeframe'; + if (!isAvailable()) { + if (args.onmissing) { + args.onmissing(); + } + + args.src = args.url || installUrl; + var mode = args.mode || 'inline'; + var preventPrompt = args.preventPrompt || false; + + if (!preventPrompt) { + if (mode == 'inline') { + makeInlinePrompt(args); + } else if (mode == 'overlay') { + makeOverlayPrompt(args); + } else { + window.open(args.src); + } + } + + if (args.preventInstallDetection) { + return; + } + + // Begin polling for install success. + var installTimer = setInterval(function() { + // every 2 seconds, look to see if CF is available, if so, proceed on + // to our destination + if (isAvailable()) { + if (args.oninstall) { + args.oninstall(); + } + + clearInterval(installTimer); + // TODO(slightlyoff): add a way to prevent navigation or make it + // contingent on oninstall? + window.location = args.destination || window.location; + } + }, 2000); + } + }; + + CFInstall._force = false; + CFInstall._forceValue = false; + CFInstall.isAvailable = isAvailable; + + // expose CFInstall to the external scope. We've already checked to make + // sure we're not going to blow existing objects away. + scope.CFInstall = CFInstall; + +})(this['ChromeFrameInstallScope'] || this); diff --git a/torah/keinim2-2.html b/torah/keinim2-2.html new file mode 100644 index 0000000..743a6c3 --- /dev/null +++ b/torah/keinim2-2.html @@ -0,0 +1,369 @@ +<!DOCTYPE html> +<html> + <head> + <title>Keinim + + + + + + + + +
+

Masechet Keinim 2:2

+

L'iluy nishmat Iser ben Binyamin HaCohen
+ לעילוי נשמת איסר בן בנימין הכהן

+

This d'var Torah is to mark my siyum- completion- of the fifth order of the mishna, Seder Kodshim, in memory of my grandfather Iser ben Binyamin HaCohen. Kodshim is paricularly appropriate, as it describes the intricacies of the temple service, from the details of an offering to the measurements of the temple mount.

+

The Mishna is broken up into six sedarim, or orders. Each order is divided into a number of masechtot, or tractates. The last tractate is called keinim, which literally means nests. It refers to the bird offerings in the temple. I would like to touch on one topic in this tractate.

+ +

Pairs

+

The most common case for bringing a bird offering was a birth, and thus the entire tractate refers to the owner of the birds in the feminine. For each birth, a woman was required to bring a pair of birds, one as an olah (burnt offering) and one as a chatat (sin offering).

+

I stress pair here because these birds must go together; it is not sufficient to have two separate bird offerings. They must be brought to the temple as a pair, and if the first is designated an olah, the other is automatically a chatat, and vice versa.

+ +

Keinim 2:2

+
Two women: Rachel has two pairs of birds, and Leah has two pairs. If one of Rachel's birds flies and is mixed in with Leah's birds, the bird invalidates one in its departure. If one returns (from Leah to Rachel), it invalidates one in its return.
+

I chose this Mishna because it has a very mathematical nature to it, something I feel connects me with my grandfather. In particular, this branch of math is known as combinatorics, or studying different possible combinations. In order to understand what's going on, let's look at a simpler case.

+ +

Four birds

+

No funny business

+

Let's say that Rachel has a single pair of birds, as does Leah. If none of the birds do any funny business, one of Rachel's birds will be an olah, and one will be a chatat. The same is true of Leah's birds. It would look like this:

+
+
+
Rachel
+
+
+
+
+
Leah
+
+
+
+
+ +

OK, some funny busines

+

Now let's say one of the birds decides to become Houdini (another Cohen if I'm not mistaken). One of Rachel's birds hops the fence into Leah's cage, and now we don't know whose bird is whose. Once a bird is assigned an owner, the owner cannot be changed (in the picture, color represents the owner). However, the type of offering (circle for olah and square for chatat) has not been assigned, and is thus up for grabs.

+

It's not possible for us to make more than two birds a chatat or more than two an olah, so the best case scenario would be that two of the three birds in Leah's possession will be an olah, the third will be a chatat, and the single bird in Rachel's cage will also be a chatat. That would look like this:

+
+
+
Rachel
+
+
+
+
Leah
+
+
+
+
+
+ +

In this case, all is fine and dandy; Leah's pair (the red birds) are an olah and chatat, and Rachel's pair (the green ones) are also an olah and chatat. No problem.

+ +

However, this assumes that we guessed correctly. Since the bird in Rachel's cage is a chatat, then we need to make sure that the green bird in Leah's cage is an olah, since we need matching pairs. We don't really know which bird actually belongs to Rachel, so we could accidently do the following:

+
+
+
Rachel
+
+
+
+
Leah
+
+
+
+
+
+ +

Now we've got a real problem! Both of Rachel's birds are chatats, and both of Leah's birds are olahs. We've got no matching pairs at all, so none of the offerings were good. It turns out the best we can do, mathematically, is to offer just two of the birds from Leah's cage. Here's the possibilities:

+ + +

Eight birds

+

Well, eight birds is basically the exact same things as four, just more complicated. To look at this mathematically, our goal is to understand:

+ + +

Proving the first is easier: we need a counter-example, or a case that could lead to mismatched pairs. Let's start off with the base case: one of Rachel's birds flew into Leah's cage:

+
+
+
Rachel
+
+
+
+
+
+
Leah
+
+
+
+
+
+
+
+ +

Doing it wrong

+

The mishna says that this disqualifies one of Rachel's birds, meaning that we sacrifice four from Leah's cage and two from Rachel's. There are two ways to sacrifice more than the allowed number:

+ +

Doing it right

+

So how do we know that the mishna's advice works? Well, let's do some deductive reasoning. Rachel has four birds all told. If she sacrifices two of them in her cage as a pair, she still has one left over to be the pair to the one in Leah's cage. If we don't sacrifice that third bird, then the fourth has the freedom to be anything it wants (within the realm of dead birds).

+
+
+
Rachel
+
+
+
+
+
+
Leah
+
+
+
+
+
+
+
+

That means that if Leah sacrifices Rachel's bird as either an olah or chatat, or doesn't sacrifice any of Rachel's birds, we're safe. There is nothing we can do to mess up Rachel's offerings. The only concern is Leah's offerings.

+

Well, if we offer four of the birds in Leah's cage, there are two possibilies:

+
    +
  1. One of the birds offered was Rachel's escapee, and one of Leah's birds was left out.
  2. +
  3. All four birds belonged to Leah, and Rachel's bird was left out.
  4. +
+

In option 1, Leah gets one matched pair and one lone offering, while Rachel gets an offering without a pair. Cool. In option 2, Leah gets two matched pairs, and Rachel gets nothing. Also good.

+ +

The grass is greener on the other side

+

Still with me? Good, we're right at the end here. So here goes the story: one of Rachel's birds flies into Leah's cage, and realizes living conditions were much better in Rachel's cage. The bird starts complaining, and eventually some bird flies into Rachel's cage. Problem is, we don't know if it was Rachel's escapee, or if one of Leah's birds has now broken out of jail.

+

What makes this different is we have two possible base cases, as follows:

+
    +
  1. +
    +
    +
    Rachel
    +
    +
    +
    +
    +
    +
    +
    Leah
    +
    +
    +
    +
    +
    +
    +
  2. +
  3. +
    +
    +
    Rachel
    +
    +
    +
    +
    +
    +
    +
    Leah
    +
    +
    +
    +
    +
    +
    +
  4. +
+

Well, if we're talking about option 1, no problem: just go 50/50 in each cage, and all is well. The problem is in option 2.

+

Independent events

+

Time to bring in more math: we have something called independent events, where a result in one case does not effect the result in another. The typical example is a coin toss: if I get heads the first time, the next toss is unaffected. Thus, if I flip a coin twice, I have four possible results:

+
    +
  1. Head, head
  2. +
  3. Head, tail
  4. +
  5. Tail, head
  6. +
  7. Tail, tail
  8. +
+

We're going to have the same thing here. In each cage, we're going to offer two birds. In Rachel's cage, we have two possible results:

+
    +
  1. Two of Rachel's birds.
  2. +
  3. One of Rachel's and one of Leah's.
  4. +
+

We have the reverse options in Leah's cage: either both Leah's, or one of Leah's and one of Rachel's. So all told, we have four possibilities.

+

Instead of displaying them all, I'll simply put in the most difficult case: where we take one from each owner from each cage. Since in this case, each owner will still have two unsacrificed birds, each bird which is sacrificed has complete freedom to be what it wants. Therefore, no problem, and all is well.

+ +

Conclusion

+

Keinim is part of Kodshim, the fifth order of the Mishna. The fourt order, Nezikin (literally meaning damages, the same root as the yiddish word mazik), discusses monetary laws.

+

If our case of the birds occurred in Nezikin, the answer would be obvious and logical: if you can't tell the birds apart anyway, who cares who's holding onto which bird? If they each started with four birds, let them each take four birds and be done with it!

+

I think this dichotomy exemplifies the distinction between the two orders. Kodshim is not about logic, or at least not human logic relating to this world. It is relating to things beyond what we can perceive. In a Kodshim perspective, the birds are connected to their owners in a way that cannot be severed. We may not see this, but Kodshim tells us it's there.

+

The juxtaposition of these two orders tells us to not compartmentalize these two approaches. We need to take both aspects with us through our daily lives. While we are fully immersed in this world, and fully subject to the laws and logic of it, we must appreciate that there is more than meets the eye.

+

While Papa may not be here in a Nezikin sense, he is still with us in a Kodshim sense. Even though we cannot see him or talk to him, we all know that the connection lives on in a way we cannot understand.

+
+ + + + diff --git a/torah/teruma.html b/torah/teruma.html new file mode 100644 index 0000000..0aa88c1 --- /dev/null +++ b/torah/teruma.html @@ -0,0 +1,156 @@ + + + + + Separating Teruma and Maaser + + + +

Separating Teruma and Maaser

+

Introduction

+

For many olim chadashim, separating teruma and maaser is an exciting- and intimidating- mitzva. There does not seem to be any straight-forward guide to doing so. Hopefully this page will fill that void.

+

In order to properly separate teruma and maaser, you must actually understand what you are doing; it is not sufficient to read a few lines in Hebrew. We'll start off by defining some terms, and then go through the step-by-step process.

+

Please note that this is meant as a guide: I am not a posek, and I am most certainly not poskening on complicated issues. This is just a guide to the basic process.

+

Definitions

+
+
Tevel
+
Produce which is obligated in teruma and maaser. Any consumption of this produce is forbidden. All produce is tevel unless: + +
+
Demai
+
Produce which might have had teruma/maaser taken. This is forbidden in consumption as well, but since it is doubtful, we do not make any brachot on its separation.
+
Teruma gedola
+
The part of the produce that was given to Cohanim in the times of the temple. Teruma can only be eaten by a cohen in tahara (purity); since we lack the requisite purity in our day, it is completely forbidden to eat. At a Torah level, there is no specific measurement given for teruma gedola.
+
Maaser rishon
+
The part of the produce that was given to Leviim in the times of the temple. Maaser rishon can be eaten by anyone. It must be precisely 10% of the produce.
+
Terumat maaser
+
The part of maaser rishon that the Levi would give to the Cohen in the times of the temple. It has the same restrictions as teruma gedola, and thus is forbidden in consumption today. It must be precisely 10% of the maaser rishon, or 1% of the total produce.
+
Maaser sheni
+
10% of the produce remaining after the removal of maaser rishon, or 9% of the total produce. Maaser sheni can be eaten by anyone, but must be eaten in purity in Jerusalem. Therefore, it is forbidden today. However, you are able to redeem maaser sheni onto money.
+
Maaser ani
+
The shemita cycle is the seven year cycle from one shemita to the next. In years 3 and 6 of the cycle, produce is obligated in maaser ani in place of maaser sheni. This is produce that must be given to a poor person.
+
Peruta
+
The smallest unit of money in the times of the gemara.
+
+

Notes

+ +

Procedure

+
    +
  1. Set aside a little bit more than 1% of the total produce. We'll call the 99% the main pile, and the 1% the side pile.
  2. +
  3. Recite the blessing on separating teruma and maaser: +

    ברוך אתה ה' אלקינו מלך העולם אשר קדשנו במצותיו וצונו להפריש תרומות ומעשרות

    +
  4. +
  5. You must now separate teruma gedola from the side pile. Remember, teruma gedola has no particular measurement. So you say: +

    The part which is more than 1% of all of the produce here, I declare it teruma gedola.

    +
  6. +
  7. Next we separate out maaser rishon. In the side pile, we have a bit of teruma gedola and 1 grape (ie, 1% of the total) of untithed produce. The maaser rishon must be 10% of the total, so we will combine the 1% from the side pile with 9% from the main pile. Without actually moving any of the produce, say: +

    The 1% which remains in this pile, together with 9 parts like it on the top of the main pile, are maaser rishon.

    +
  8. +
  9. From our maaser rishon, we must separate terumat maaser. In the previous step, we designated the 1% in the side pile to be maaser rishon; we will now designate it to be terumat maaser: +

    That same 1% which I made maaser rishon is terumat maaser.

    + The side pile now consists entirely of teruma gedola and terumat maaser, which is forbidden in consumption.
  10. +
  11. All that remains in maaser sheni/maaser ani. This works out to 9% of the total. Since it may be unclear which type of maaser you should be separating now, the following statement is made conditionally: +

    9 more parts like these in the bottom side of the main pile are maaser sheni, and if they are obligated in maaser ani, they are maaser ani.

    +
  12. +
  13. You now must redeem the maaser sheni/maaser ani in the produce. In order to redeem the produce, you must "pay a fee" of a fifth, which due to the differences in modern math and Torah math is what we call a fourth. In any event, the value of the produce and its fifth is all considered to be part of the coin you will be using. First make the bracha (if appropriate): +

    ברוך אתה ה' אלקינו מלך העולם אשר קדשנו במצותיו וצונו על פדיון מעשר שני

    + and then take that coin and say: +

    This maaser sheni, it and its fifth, are redeemed on one peruta from the coin which I have designated for the redemption of maaser sheni.

    +
  14. +
  15. Wrap up the coin with the side pile very well. All of it must be disposed of properly, meaning double wrapped in the trash. No benefit should be derived from either the coin or the produce.
  16. +
+

That's all there is to it.

+
Produced by Michael Snoyman
+ + +