├── .gitignore ├── AUTHORS ├── COPYING ├── Cargo.toml ├── README.md └── src ├── ch01a_before.rs ├── ch01b_new_method.rs ├── ch01c_sad_face.rs ├── ch02_open_sum.rs ├── ch03_evaluation.rs ├── ch04_smart_constructors.rs ├── ch05a_multiplication.rs ├── ch05b_display.rs ├── ch06_calculator_monad.rs ├── ch07a_pairs.rs ├── ch07b_generic_evaluation.rs ├── ch07c_pair_evaluation.rs ├── ch07d_safer_pair_evaluation.rs ├── ch08a_expressions.rs ├── ch08b_open_recursion_evaluation.rs ├── lib.rs └── old.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Douglas Creager 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "expression-problem" 3 | version = "0.1.0" 4 | authors = ["Douglas Creager "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exploring the "expression problem" in Rust 2 | 3 | Let's explore some interesting papers in Rust! 4 | 5 | - ["Data types à la carte"][data-types], Wouter Swierstra. 6 | - ["Compositional data types"][compositional], Patrick Bahr, Tom Hvitved. 7 | 8 | These papers all discuss the "expression problem", as described by [Phil 9 | Wadler][expression-problem]: 10 | 11 | > The goal is to define a data type by cases, where one can add new cases to the data type 12 | and new functions over the data type, without recompiling existing code, and while retaining 13 | static type safety. 14 | 15 | [compositional]: http://bahr.io/pubs/files/bahr11wgp-paper.pdf 16 | [data-types]: http://www.cs.ru.nl/%7EW.Swierstra/Publications/DataTypesALaCarte.pdf 17 | [expression-problem]: http://homepages.inf.ed.ac.uk/wadler/papers/expression/expression.txt 18 | 19 | Since these are FP papers, they all present solutions in Haskell. I'd like to 20 | explore the problem in Rust! Unlike some other "lets implement some papers in 21 | language X" explorations, my goal here is **_not_** to translate the Haskell 22 | solutions into Rust; instead, I want to see what a *Rust solution* would look 23 | like. Partly that's because (at least as of right now) we don't have some of 24 | the same building blocks available in Rust (e.g., functors, higher-kinded 25 | types). But my hunch is that we have *different* building blocks that let us 26 | produce solutions with the same properties. Let's see if that's true! 27 | 28 | Since a big part of the expression problem talks about what you can do without 29 | editing existing code, I'm going to implement this is a bunch of different Rust 30 | modules. That will help enforce that we're building new capabilities by only 31 | writing new code, and not by editing any existing code. 32 | 33 | ### Data types à la carte 34 | 35 | #### §1: Introduction 36 | 37 | - [ch01a\_before](src/ch01a_before.rs): We create a toy language and an AST for 38 | it, and write some evaluation rules. 39 | 40 | - [ch01b\_new\_method](src/ch01b_new_method.rs): We add a new method that 41 | operates on our AST. 42 | 43 | - [ch01c\_sad\_face](src/ch01c_sad_face.rs): We try to add a new kind of term to 44 | the language, and get pretty far before running into a brick wall. 45 | 46 | #### §2: Fixing the expression problem 47 | 48 | - [ch02\_open\_sum](src/ch02_open_sum.rs): Create separate types for each kind 49 | of term, instead of force-glomming them into a single enum type. Define a 50 | generic `Sum` type to be able to pick and choose which ones to include. 51 | 52 | #### §3: Evaluation 53 | 54 | - [ch03\_evaluation][]: Define evaluation using a trait, 55 | with an impl of that trait for each of our terms. 56 | 57 | [ch03\_evaluation]: src/ch03_evaluation.rs 58 | 59 | #### §4: Automating injections 60 | 61 | - [ch04\_smart\_constructors](src/ch04_smart_constructors.rs): Make it not so 62 | hideously ugly to create instances of our new types. 63 | 64 | #### §5: Examples 65 | 66 | - [ch05a\_multiplication](src/ch05a_multiplication.rs): It's pretty easy to add 67 | a new kind of term! 68 | 69 | - [ch05b\_display](src/ch05b_display.rs): It's also pretty easy to add a new 70 | function that operates on all of the terms we've defined so far! 71 | 72 | #### §6: Monads for free 73 | 74 | - [ch06\_calculator\_monad](src/ch06_calculator_monad.rs): In Rust, the monads 75 | are even more free? Since we don't really need them? 76 | 77 | ### Compositional data types 78 | 79 | #### §2.1 Evaluating expressions 80 | 81 | - [ch07a\_pairs](src/ch07a_pairs.rs): Let's add a new type of value to our 82 | language. 83 | 84 | - [ch07b\_generic\_evaluation][]: How could we have done a better job of 85 | defining our evaluation rules from [ch03\_evaluation][] — so that we could 86 | reuse them as-is with our new pair-equipped language? 87 | 88 | [ch07b\_generic\_evaluation]: src/ch07b_generic_evaluation.rs 89 | 90 | - [ch07c\_pair\_evaluation](src/ch07c_pair_evaluation.rs): And now we can add 91 | evaluation rules for pairs too! And we can reuse the existing evaluation 92 | rules for integers, even though we now have a more complicated value type. 93 | 94 | #### §3.2 Monadic computations 95 | 96 | - [ch07d\_safer\_pair\_evaluation](src/ch07d_safer_pair_evaluation.rs): Let's 97 | evaluate pairs again, without panicking when we encounter a type error. No 98 | changes needed to the evaluation rules — we just need to use a value type that 99 | can encode errors! 100 | 101 | ### Eliminating boilerplate 102 | 103 | - [ch08a\_expressions](src/ch08a_expressions.rs): This was all very fun, but it 104 | required more boilerplate than we needed on the Haskell side. Can we get rid 105 | of it? First let's create a Rust trait that lines up with the `Expr` type 106 | family. This will let us talk about expressions in any of our mini-languages 107 | generically. 108 | 109 | - [ch08b\_open\_recursion\_evaluation](src/ch08b_open_recursion_evaluation.rs): 110 | Blammo! With a little tweak to how we do the recursion, we can update the 111 | evaluation rules from [ch07b\_generic\_evaluation][] so that they work out of 112 | the box for **any** type that implements `Expression`. 113 | -------------------------------------------------------------------------------- /src/ch01a_before.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2018-2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | /// We can use an enum to represent all of the different kinds of term that can appear in our toy 17 | /// language. 18 | pub enum Expression { 19 | /// An integer constant with a particular value (i.e., we've already parsed whatever string 20 | /// representations are allowed in our source language; in the AST, we stored the parsed value 21 | /// of the constant). 22 | IntegerLiteral(i64), 23 | /// To add two numbers together, we need the ASTs of the left- and right-hand sides; to make 24 | /// this recursion work, we have to Box them so that we don't have an infinitely large type. 25 | Add(Box, Box), 26 | /// Ditto for subtraction. 27 | Subtract(Box, Box), 28 | } 29 | 30 | /// Then we can implement an evaluation function that returns the value of any expression in our 31 | /// language. We use a `match` statement to define how to evaluate each kind of term. 32 | impl Expression { 33 | pub fn evaluate(&self) -> i64 { 34 | match self { 35 | Expression::IntegerLiteral(value) => *value, 36 | Expression::Add(lhs, rhs) => lhs.evaluate() + rhs.evaluate(), 37 | Expression::Subtract(lhs, rhs) => lhs.evaluate() - rhs.evaluate(), 38 | } 39 | } 40 | } 41 | 42 | // Some smart constructors. Note that we don't require the return value to be Expression; it can 43 | // be anything that we can create from an Expression. This will be needed in ch01c. 44 | 45 | pub fn integer_literal>(value: i64) -> E { 46 | E::from(Expression::IntegerLiteral(value)) 47 | } 48 | 49 | pub fn add>(lhs: Expression, rhs: Expression) -> E { 50 | E::from(Expression::Add(Box::new(lhs), Box::new(rhs))) 51 | } 52 | 53 | pub fn subtract>(lhs: Expression, rhs: Expression) -> E { 54 | E::from(Expression::Subtract(Box::new(lhs), Box::new(rhs))) 55 | } 56 | 57 | #[cfg(test)] 58 | mod tests { 59 | use super::*; 60 | 61 | #[test] 62 | fn can_evaluate_integer_literal() { 63 | // The type annotation is needed because of how we're using the From trait in our smart 64 | // constructors. 65 | let one: Expression = integer_literal(1); 66 | assert_eq!(one.evaluate(), 1); 67 | } 68 | 69 | #[test] 70 | fn can_evaluate_add() { 71 | let add: Expression = add(integer_literal(1), integer_literal(2)); 72 | assert_eq!(add.evaluate(), 3); 73 | } 74 | 75 | #[test] 76 | fn can_evaluate_subtract() { 77 | let subtract: Expression = subtract(integer_literal(1), integer_literal(2)); 78 | assert_eq!(subtract.evaluate(), -1); 79 | } 80 | 81 | #[test] 82 | fn can_evaluate_nested() { 83 | let add: Expression = add( 84 | integer_literal(1), 85 | subtract(integer_literal(2), integer_literal(3)), 86 | ); 87 | assert_eq!(add.evaluate(), 0); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/ch01b_new_method.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2018-2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | // We've defined the AST representation of our language in another module. Our goal is to add a 17 | // new method that operates on those ASTs without touching the original definitions. 18 | use crate::ch01a_before::*; 19 | 20 | use std::fmt; 21 | 22 | /// No problem! We can't use this definition of std::fmt::Display back in ch01a_before, but that's 23 | /// okay, since we don't need to. 24 | impl fmt::Display for Expression { 25 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 26 | match self { 27 | Expression::IntegerLiteral(value) => write!(f, "{}", value), 28 | Expression::Add(lhs, rhs) => write!(f, "({} + {})", lhs, rhs), 29 | Expression::Subtract(lhs, rhs) => write!(f, "({} - {})", lhs, rhs), 30 | } 31 | } 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use super::*; 37 | 38 | #[test] 39 | fn can_evaluate_integer_literal() { 40 | let one: Expression = integer_literal(1); 41 | assert_eq!(format!("{}", one), "1"); 42 | } 43 | 44 | #[test] 45 | fn can_evaluate_add() { 46 | let add: Expression = add(integer_literal(1), integer_literal(2)); 47 | assert_eq!(format!("{}", add), "(1 + 2)"); 48 | } 49 | 50 | #[test] 51 | fn can_evaluate_subtract() { 52 | let subtract: Expression = subtract(integer_literal(1), integer_literal(2)); 53 | assert_eq!(format!("{}", subtract), "(1 - 2)"); 54 | } 55 | 56 | #[test] 57 | fn can_evaluate_nested() { 58 | let add: Expression = add( 59 | integer_literal(1), 60 | subtract(integer_literal(2), integer_literal(3)), 61 | ); 62 | assert_eq!(format!("{}", add), "(1 + (2 - 3))"); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/ch01c_sad_face.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2018-2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | // We've defined the AST representation of our language in another module. Our goal is to add a 17 | // new kind of term to the language — without editing or copying the original definitions. 18 | // 19 | // Note that we're not using a * import so that we can be clear whether we're referring to the new 20 | // Expression, which includes the new kind of term, or the old one, which doesn't. 21 | use crate::ch01a_before; 22 | 23 | /// This is the closest we can get: we create a new Expression type, containing each of the new 24 | /// kinds of term, and use a wrapper variant at the end to include all of the old terms without 25 | /// duplicating their definitions. 26 | pub enum Expression { 27 | /// We can now negate numbers, too! 28 | Negate(Box), 29 | /// But we want to be able to use all of the existing terms without copying their definitions. 30 | Existing(ch01a_before::Expression), 31 | } 32 | 33 | /// We can wrap any old expression in our new AST type. 34 | impl std::convert::From for Expression { 35 | fn from(wrapped: ch01a_before::Expression) -> Expression { 36 | Expression::Existing(wrapped) 37 | } 38 | } 39 | 40 | /// We can implement a new evaluate function that knows what to do with the new kind of term, but 41 | /// which delegates to the existing function for all of the existing kinds of term. 42 | impl Expression { 43 | pub fn evaluate(&self) -> i64 { 44 | match self { 45 | Expression::Negate(nested) => -nested.evaluate(), 46 | Expression::Existing(existing) => existing.evaluate(), 47 | } 48 | } 49 | } 50 | 51 | // We can use the existing smart constructors as-is because of how we used From. 52 | pub use crate::ch01a_before::add; 53 | pub use crate::ch01a_before::integer_literal; 54 | pub use crate::ch01a_before::subtract; 55 | 56 | // And then a smart constructor for the new term 57 | 58 | pub fn negate(nested: Expression) -> Expression { 59 | Expression::Negate(Box::new(nested)) 60 | } 61 | 62 | #[cfg(test)] 63 | mod tests { 64 | use super::*; 65 | 66 | // All of the old smart constructors and evaluation rules Just Work: 67 | 68 | #[test] 69 | fn can_evaluate_integer_literal() { 70 | let one: Expression = integer_literal(1); 71 | assert_eq!(one.evaluate(), 1); 72 | } 73 | 74 | #[test] 75 | fn can_evaluate_add() { 76 | let add: Expression = add(integer_literal(1), integer_literal(2)); 77 | assert_eq!(add.evaluate(), 3); 78 | } 79 | 80 | #[test] 81 | fn can_evaluate_subtract() { 82 | let subtract: Expression = subtract(integer_literal(1), integer_literal(2)); 83 | assert_eq!(subtract.evaluate(), -1); 84 | } 85 | 86 | #[test] 87 | fn can_evaluate_nested() { 88 | let add: Expression = add( 89 | integer_literal(1), 90 | subtract(integer_literal(2), integer_literal(3)), 91 | ); 92 | assert_eq!(add.evaluate(), 0); 93 | } 94 | 95 | // And so do the new ones: 96 | 97 | #[test] 98 | fn can_evaluate_negate() { 99 | let negate: Expression = negate(integer_literal(1)); 100 | assert_eq!(negate.evaluate(), -1); 101 | } 102 | 103 | #[test] 104 | fn can_evaluate_nested_negate() { 105 | let negate: Expression = negate(subtract(integer_literal(2), integer_literal(3))); 106 | assert_eq!(negate.evaluate(), 1); 107 | } 108 | 109 | // But! We cannot put a negation inside of any of the old kinds of terms! 110 | 111 | /* 112 | #[test] 113 | fn cannot_evaluate_negate_inside_add() { 114 | // This line won't compile! 115 | let add: Expression = add(integer_literal(1), negate(integer_literal(2))); 116 | assert_eq!(negate.evaluate(), -1); 117 | } 118 | */ 119 | } 120 | -------------------------------------------------------------------------------- /src/ch02_open_sum.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2018-2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! Instead of having a single Expression enum, with different variants for each kind of term, we 17 | //! create a separate type for each kind of term. We're going to name each type the same as the 18 | //! enum variant from ch01a, to make it clear how they line up. 19 | 20 | /// An integer constant with a particular value. Note that unlike in the paper, and unlike the Add 21 | /// and Subtract terms below, this is **not** parameterized by the `e` type! We don't have 22 | /// functors in Rust, and so we don't need to force each of our term representations to have the 23 | /// same kind. 24 | pub struct IntegerLiteral { 25 | pub value: i64, 26 | } 27 | 28 | /// We can add two expressions together, but since we don't have an Expression type (yet), we don't 29 | /// know what type the left- and right-hand sides should have. Let's punt for now, and take that 30 | /// in as a generic type parameter. (Just like Swierstra does in the paper!) 31 | pub struct Add { 32 | pub lhs: E, 33 | pub rhs: E, 34 | } 35 | 36 | /// This is how we'll create the different Expression types from ch01! This corresponds to the :+: 37 | /// "coproduct" operator from the paper. 38 | pub enum Sum { 39 | Left(L), 40 | Right(R), 41 | } 42 | 43 | // To create the analogue of `Expr (Val :+: Add)` in Rust, we'd ideally want to do: 44 | // 45 | // pub type Expr = Sum>>; 46 | // 47 | // But that won't compile, since you end up with a cycle in the type expansion. We end up having 48 | // to define the `Val :+: Add` part and the `Expr` wrapper separately: 49 | 50 | pub type Sig = Sum>; 51 | pub struct Expr(pub Box>); 52 | 53 | #[cfg(test)] 54 | mod tests { 55 | use super::*; 56 | 57 | #[test] 58 | fn can_instantiate_ugly_expression() { 59 | // 118 + 1219 60 | // This is ugly, but we can instantiate it! 61 | let _: Expr = Expr(Box::new(Sum::Right(Add:: { 62 | lhs: Expr(Box::new(Sum::Left(IntegerLiteral { value: 118 }))), 63 | rhs: Expr(Box::new(Sum::Left(IntegerLiteral { value: 1219 }))), 64 | }))); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ch03_evaluation.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2018-2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! And now let's add some evaluation rules for the new types. 17 | 18 | use crate::ch02_open_sum::*; 19 | 20 | // Again, we don't have functors available, so our evaluation trait in Rust won't exactly line up 21 | // with the typeclass and recursion scheme from Haskell. But it will have the same capabilities, 22 | // and its definition won't be any more complex. 23 | 24 | /// Each kind of term in our language should implement this trait to define how it's evaluated. 25 | pub trait EvaluateInt { 26 | fn evaluate(&self) -> i64; 27 | } 28 | 29 | /// Integer literals evaluate to their value. 30 | impl EvaluateInt for IntegerLiteral { 31 | fn evaluate(&self) -> i64 { 32 | self.value 33 | } 34 | } 35 | 36 | impl EvaluateInt for Add 37 | where 38 | // We can only evaluate an addition if we know how to evaluate its subexpressions. 39 | E: EvaluateInt, 40 | { 41 | fn evaluate(&self) -> i64 { 42 | self.lhs.evaluate() + self.rhs.evaluate() 43 | } 44 | } 45 | 46 | /// We can evaluate a sum if we know how to evaluate both of its variants; we just delegate to the 47 | /// underlying type's impl. 48 | impl EvaluateInt for Sum 49 | where 50 | // But of course, we need to be able to evaluate each of the possible subexpressions. 51 | L: EvaluateInt, 52 | R: EvaluateInt, 53 | { 54 | fn evaluate(&self) -> i64 { 55 | match self { 56 | Sum::Left(lhs) => lhs.evaluate(), 57 | Sum::Right(rhs) => rhs.evaluate(), 58 | } 59 | } 60 | } 61 | 62 | // Since we don't have functors, we can't define a generic `foldExpr` that works for any expression 63 | // type; instead, we have a small amount of boilerplate. Note that `Sig` does get an `EvaluateInt` 64 | // impl for free — it's just an alias for a particular `Sum`, after all. 65 | 66 | impl EvaluateInt for Expr { 67 | fn evaluate(&self) -> i64 { 68 | self.0.evaluate() 69 | } 70 | } 71 | 72 | #[cfg(test)] 73 | mod tests { 74 | use super::*; 75 | 76 | #[test] 77 | fn can_evaluate_ugly_expression() { 78 | // 118 + 1219 79 | let add: Expr = Expr(Box::new(Sum::Right(Add:: { 80 | lhs: Expr(Box::new(Sum::Left(IntegerLiteral { value: 118 }))), 81 | rhs: Expr(Box::new(Sum::Left(IntegerLiteral { value: 1219 }))), 82 | }))); 83 | assert_eq!(add.evaluate(), 1337); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/ch04_smart_constructors.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2018-2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! Let's make it not hideously ugly to create instances of our expression type. 17 | 18 | use crate::ch02_open_sum::*; 19 | 20 | // In Rust, we already have the equivalent of the :<: typeclass. It's called std::convert::From! 21 | // So we just need to define an impl for our Sum type. 22 | // 23 | // Complicating things is that these two impls overlap. In the paper, Swierstra runs into the same 24 | // difficulty, and relies on a Haskell extension that allows overlapping instances of typeclasses. 25 | // Rust has something similar in #![feature(specialization)], but it unfortunately has more 26 | // restrictions and doesn't work for this example. Instead, we need to add some extra constraints 27 | // to the second impl to make them no longer conflict. These extra constraints rely on 28 | // #![feature(optin_builtin_traits)] to define NotEq, which lets us assert that some of the type 29 | // variables in the second impl represent distinct types. 30 | // 31 | // Also note that, like in the paper, we expect the Sum type to be used in a "list-like", 32 | // right-associative fashion. That is, if you want the sum of A, B, or C, you need to use `Sum>`, and not `Sum, C>`. 34 | 35 | pub auto trait NotEq {} 36 | impl !NotEq for (X, X) {} 37 | 38 | impl From for Sum { 39 | fn from(left: L) -> Sum { 40 | Sum::Left(left) 41 | } 42 | } 43 | 44 | impl From for Sum 45 | where 46 | R: From, 47 | (X, L): NotEq, 48 | (X, Self): NotEq, 49 | { 50 | fn from(x: X) -> Sum { 51 | Sum::Right(R::from(x)) 52 | } 53 | } 54 | 55 | // And like EvaluateInt, we have to explicitly write an impl for our Expr type. 56 | impl From for Expr 57 | where 58 | Sig: From, 59 | { 60 | fn from(x: X) -> Expr { 61 | Expr(Box::new(Sig::::from(x))) 62 | } 63 | } 64 | 65 | // With those impls in place, we can define smart constructors like we did in ch01. 66 | 67 | pub fn integer_literal>(value: i64) -> E { 68 | E::from(IntegerLiteral { value }) 69 | } 70 | 71 | pub fn add>>(lhs: E, rhs: E) -> E { 72 | E::from(Add { lhs, rhs }) 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests { 77 | use super::*; 78 | use crate::ch03_evaluation::*; 79 | 80 | #[test] 81 | fn can_evaluate_ugly_expression() { 82 | // 118 + 1219 83 | // Much nicer! 84 | let add: Expr = add(integer_literal(118), integer_literal(1219)); 85 | assert_eq!(add.evaluate(), 1337); 86 | } 87 | 88 | #[test] 89 | fn can_evaluate_nested_expression() { 90 | // 30000 + 1330 + 7 91 | let add: Expr = add( 92 | integer_literal(30000), 93 | add(integer_literal(1330), integer_literal(7)), 94 | ); 95 | assert_eq!(add.evaluate(), 31337); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/ch05a_multiplication.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2018-2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! Look how easy it is to add a new term! 17 | 18 | use crate::ch02_open_sum::*; 19 | use crate::ch03_evaluation::*; 20 | 21 | /// First a type for the new term 22 | pub struct Multiply { 23 | pub lhs: E, 24 | pub rhs: E, 25 | } 26 | 27 | /// Then an evaluation rule for it 28 | impl EvaluateInt for Multiply 29 | where 30 | // We can only evaluate an addition if we know how to evaluate its subexpressions. 31 | E: EvaluateInt, 32 | { 33 | fn evaluate(&self) -> i64 { 34 | self.lhs.evaluate() * self.rhs.evaluate() 35 | } 36 | } 37 | 38 | /// And a smart constructor 39 | pub fn multiply>>(lhs: E, rhs: E) -> E { 40 | E::from(Multiply { lhs, rhs }) 41 | } 42 | 43 | // And then an expression that can contain it, along with the existing terms. 44 | pub type MultSig = Sum, Sig>; 45 | pub struct MultExpr(pub Box>); 46 | 47 | impl EvaluateInt for MultExpr { 48 | fn evaluate(&self) -> i64 { 49 | self.0.evaluate() 50 | } 51 | } 52 | 53 | impl From for MultExpr 54 | where 55 | MultSig: From, 56 | { 57 | fn from(x: X) -> MultExpr { 58 | MultExpr(Box::new(MultSig::::from(x))) 59 | } 60 | } 61 | 62 | // And to show off, we can create an expression that isn't allowed to contain addition! 63 | pub type NoAddSig = Sum>; 64 | pub struct NoAddExpr(pub Box>); 65 | 66 | impl EvaluateInt for NoAddExpr { 67 | fn evaluate(&self) -> i64 { 68 | self.0.evaluate() 69 | } 70 | } 71 | 72 | impl From for NoAddExpr 73 | where 74 | NoAddSig: From, 75 | { 76 | fn from(x: X) -> NoAddExpr { 77 | NoAddExpr(Box::new(NoAddSig::::from(x))) 78 | } 79 | } 80 | 81 | #[cfg(test)] 82 | mod tests { 83 | use super::*; 84 | use crate::ch04_smart_constructors::*; 85 | 86 | #[test] 87 | fn can_evaluate_multiplication() { 88 | let mult: MultExpr = add( 89 | multiply(integer_literal(80), integer_literal(5)), 90 | integer_literal(4), 91 | ); 92 | assert_eq!(mult.evaluate(), 404); 93 | } 94 | 95 | #[test] 96 | fn can_evaluate_no_add_multiplication() { 97 | let mult: NoAddExpr = multiply(integer_literal(6), integer_literal(7)); 98 | assert_eq!(mult.evaluate(), 42); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/ch05b_display.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2018-2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! We can add a new function that operates on all of these terms that we've created so far. 17 | 18 | use crate::ch02_open_sum::*; 19 | use crate::ch05a_multiplication::*; 20 | 21 | use std::fmt; 22 | 23 | // Add an impl for each term. 24 | 25 | impl fmt::Display for IntegerLiteral { 26 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 27 | self.value.fmt(f) 28 | } 29 | } 30 | 31 | impl fmt::Display for Add 32 | where 33 | E: fmt::Display, 34 | { 35 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 36 | write!(f, "({} + {})", self.lhs, self.rhs) 37 | } 38 | } 39 | 40 | impl fmt::Display for Multiply 41 | where 42 | E: fmt::Display, 43 | { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | write!(f, "({} * {})", self.lhs, self.rhs) 46 | } 47 | } 48 | 49 | // And one for the open sum! 50 | 51 | impl fmt::Display for Sum 52 | where 53 | L: fmt::Display, 54 | R: fmt::Display, 55 | { 56 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 57 | match self { 58 | Sum::Left(lhs) => lhs.fmt(f), 59 | Sum::Right(rhs) => rhs.fmt(f), 60 | } 61 | } 62 | } 63 | 64 | // And then the boilerplate impl for each expression type. 65 | 66 | impl fmt::Display for Expr { 67 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 68 | self.0.fmt(f) 69 | } 70 | } 71 | 72 | impl fmt::Display for MultExpr { 73 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 74 | self.0.fmt(f) 75 | } 76 | } 77 | 78 | impl fmt::Display for NoAddExpr { 79 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 80 | self.0.fmt(f) 81 | } 82 | } 83 | 84 | #[cfg(test)] 85 | mod tests { 86 | use super::*; 87 | use crate::ch04_smart_constructors::*; 88 | 89 | #[test] 90 | fn can_render_ugly_expression() { 91 | let add: Expr = add(integer_literal(118), integer_literal(1219)); 92 | assert_eq!(format!("{}", add), "(118 + 1219)"); 93 | } 94 | 95 | #[test] 96 | fn can_render_multiplication() { 97 | let mult: MultExpr = add( 98 | multiply(integer_literal(80), integer_literal(5)), 99 | integer_literal(4), 100 | ); 101 | assert_eq!(format!("{}", mult), "((80 * 5) + 4)"); 102 | } 103 | 104 | #[test] 105 | fn can_evaluate_no_add_multiplication() { 106 | let mult: NoAddExpr = multiply(integer_literal(6), integer_literal(7)); 107 | assert_eq!(format!("{}", mult), "(6 * 7)"); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/ch06_calculator_monad.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! I'm going to make the bold claim that most of Swierstra §6 isn't relevant in Rust — we don't 17 | //! typically use monads to express stateful computations, we just write Rust code. And traits 18 | //! give us the means to express the requirements of a function piecewise. 19 | 20 | /// A memory store can be incremented by a delta value, but this requires mutable access to it. 21 | pub trait Increment { 22 | fn increment(&mut self, delta: i64) -> (); 23 | } 24 | 25 | /// If you only want to read the contents of the memory, you can get away with non-mutable access 26 | /// to it. 27 | pub trait Recall { 28 | fn recall(&self) -> i64; 29 | } 30 | 31 | /// The simplest memory store is just a struct containing the current contents. 32 | pub struct Mem { 33 | value: i64, 34 | } 35 | 36 | impl Increment for Mem { 37 | fn increment(&mut self, delta: i64) -> () { 38 | self.value += delta; 39 | } 40 | } 41 | 42 | impl Recall for Mem { 43 | fn recall(&self) -> i64 { 44 | self.value 45 | } 46 | } 47 | 48 | /// The tick function from the paper needs both operations, and Rust's trait bounds already give us 49 | /// a great language for expressing that constraint. 50 | pub fn tick(mem: &mut M) -> i64 51 | where 52 | M: Increment + Recall, 53 | { 54 | let y = mem.recall(); 55 | mem.increment(1); 56 | y 57 | } 58 | 59 | /// This function (which doesn't appear in the paper) only needs the Recall operation, and as such, 60 | /// works with a non-mutable reference to the store. 61 | pub fn get(mem: &M) -> i64 62 | where 63 | M: Recall, 64 | { 65 | mem.recall() 66 | } 67 | 68 | #[cfg(test)] 69 | mod tests { 70 | use super::*; 71 | 72 | #[test] 73 | fn can_run_tick() { 74 | let mut mem = Mem { value: 4 }; 75 | let result = tick(&mut mem); 76 | assert_eq!(result, 4); 77 | assert_eq!(mem.value, 5); 78 | } 79 | 80 | #[test] 81 | fn can_run_get() { 82 | assert_eq!(get(&Mem { value: 4 }), 4); 83 | assert_eq!(get(&Mem { value: 10 }), 10); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/ch07a_pairs.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! Let's add pairs to the language! Note that we've switched from "Data types à la carte" over to 17 | //! "Compositional data types". 18 | 19 | use crate::ch02_open_sum::*; 20 | 21 | /// Creates a new pair, whose contents are given by two subexpressions. 22 | pub struct Pair { 23 | pub first: E, 24 | pub second: E, 25 | } 26 | 27 | /// Extract the first element of a pair. 28 | pub struct First { 29 | pub pair: E, 30 | } 31 | 32 | /// Extract the second element of a pair. 33 | pub struct Second { 34 | pub pair: E, 35 | } 36 | 37 | // And some smart constructors 38 | 39 | pub fn pair>>(first: E, second: E) -> E { 40 | E::from(Pair { first, second }) 41 | } 42 | 43 | pub fn first>>(pair: E) -> E { 44 | E::from(First { pair }) 45 | } 46 | 47 | pub fn second>>(pair: E) -> E { 48 | E::from(Second { pair }) 49 | } 50 | 51 | // All of these nested Sums are getting cumbersome. Let's add a macro. 52 | 53 | macro_rules! Sum { 54 | { $A:ty, $B:ty } => { Sum<$A, $B> }; 55 | { $A:ty, $($B:ty),+ } => { Sum<$A, Sum![$($B),+]> }; 56 | } 57 | 58 | // Now we create an expression type that can include pairs. 59 | 60 | pub type PairSig = Sum![Pair, First, Second, Sig]; 61 | pub struct PairExpr(pub Box>); 62 | 63 | impl From for PairExpr 64 | where 65 | PairSig: From, 66 | { 67 | fn from(x: X) -> PairExpr { 68 | PairExpr(Box::new(PairSig::::from(x))) 69 | } 70 | } 71 | 72 | #[cfg(test)] 73 | mod tests { 74 | use super::*; 75 | use crate::ch04_smart_constructors::*; 76 | 77 | #[test] 78 | fn can_create_pair_expressions() { 79 | let _: PairExpr = first(pair(integer_literal(7), integer_literal(6))); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/ch07b_generic_evaluation.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! Our existing evaluation trait doesn't work with our new expressions, since the result can only 17 | //! be an integer. We need a result type that allows pairs, as well! Can we construct an 18 | //! evaluation trait that lets evaluate the old Expr type (which doesn't include pairs), but which 19 | //! we can extend in a different module to work with PairExpr and pairs? (Yes.) 20 | 21 | use crate::ch02_open_sum::*; 22 | 23 | /// Well that was easy. (Not really! Don't worry, we'll run into wrinkles.) 24 | pub trait EvaluateAny { 25 | fn evaluate(&self) -> V; 26 | } 27 | 28 | /// This helper function will make things easier for us when we try to call our evaluate trait 29 | /// method. (We'll have to explicitly call out which value type we want to use, and it's somewhat 30 | /// less verbose to do that on a function than trying to cast our Expr type to the right trait 31 | /// instantiation.) 32 | pub fn evaluate_any(expr: &E) -> V 33 | where 34 | E: EvaluateAny, 35 | { 36 | expr.evaluate() 37 | } 38 | 39 | /// Integer literals evaluate to their value. We can lift them into any result type that can be 40 | /// constructed from an integer. 41 | impl EvaluateAny for IntegerLiteral 42 | where 43 | V: From, 44 | { 45 | fn evaluate(&self) -> V { 46 | V::from(self.value) 47 | } 48 | } 49 | 50 | impl EvaluateAny for Add 51 | where 52 | // We can only evaluate an addition if we know how to evaluate its subexpressions. 53 | E: EvaluateAny, 54 | // and if the result type can itself be added together! 55 | V: std::ops::Add, 56 | { 57 | fn evaluate(&self) -> V { 58 | self.lhs.evaluate() + self.rhs.evaluate() 59 | } 60 | } 61 | 62 | /// We can evaluate a sum if we know how to evaluate both of its variants; we just delegate to the 63 | /// underlying type's impl. 64 | impl EvaluateAny for Sum 65 | where 66 | L: EvaluateAny, 67 | R: EvaluateAny, 68 | { 69 | fn evaluate(&self) -> V { 70 | match self { 71 | Sum::Left(lhs) => lhs.evaluate(), 72 | Sum::Right(rhs) => rhs.evaluate(), 73 | } 74 | } 75 | } 76 | 77 | /// Like before, we have to explicitly provide an EvaluateAny impl for our expression types. The 78 | /// main wrinkle is that we **also** have to explicitly carry over any of the constraints that the 79 | /// individual terms require of the value type — Rust won't propagate those for us. 80 | impl EvaluateAny for Expr 81 | where 82 | V: From + std::ops::Add, 83 | { 84 | fn evaluate(&self) -> V { 85 | self.0.evaluate() 86 | } 87 | } 88 | 89 | #[cfg(test)] 90 | mod tests { 91 | use super::*; 92 | use crate::ch04_smart_constructors::*; 93 | 94 | #[test] 95 | fn can_evaluate_ugly_expression() { 96 | // 118 + 1219 97 | let add: Expr = add(integer_literal(118), integer_literal(1219)); 98 | // Kind of gross 99 | assert_eq!((&add as &EvaluateAny).evaluate(), 1337); 100 | // A little bit nicer 101 | assert_eq!(evaluate_any::(&add), 1337); 102 | } 103 | 104 | #[test] 105 | fn can_evaluate_nested_expression() { 106 | // 30000 + 1330 + 7 107 | let add: Expr = add( 108 | integer_literal(30000), 109 | add(integer_literal(1330), integer_literal(7)), 110 | ); 111 | assert_eq!((&add as &EvaluateAny).evaluate(), 31337); 112 | assert_eq!(evaluate_any::(&add), 31337); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/ch07c_pair_evaluation.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! Now we should be able to define evaluation rules for pairs, where the results might be pairs or 17 | //! integers, and reuse the evaluation rules for all of the existing terms. 18 | 19 | use crate::ch07a_pairs::*; 20 | use crate::ch07b_generic_evaluation::*; 21 | 22 | /// We have pairs in Rust, so we can use that when defining the result for a pair expression. Just 23 | /// like integer literals, we can lift the Rust pairs into the result type as long as it has the 24 | /// right From impl. 25 | impl EvaluateAny for Pair 26 | where 27 | E: EvaluateAny, 28 | V: From<(V, V)>, 29 | { 30 | fn evaluate(&self) -> V { 31 | V::from((self.first.evaluate(), self.second.evaluate())) 32 | } 33 | } 34 | 35 | /// We don't have a trait that we can reuse for the evaluation rules for First and Second, like we 36 | /// could with std::ops::Add for our Add term. So let's make one! 37 | pub trait ProjectPair { 38 | fn first(self) -> Self; 39 | fn second(self) -> Self; 40 | } 41 | 42 | impl EvaluateAny for First 43 | where 44 | E: EvaluateAny, 45 | V: ProjectPair, 46 | { 47 | fn evaluate(&self) -> V { 48 | self.pair.evaluate().first() 49 | } 50 | } 51 | 52 | impl EvaluateAny for Second 53 | where 54 | E: EvaluateAny, 55 | V: ProjectPair, 56 | { 57 | fn evaluate(&self) -> V { 58 | self.pair.evaluate().second() 59 | } 60 | } 61 | 62 | /// And the EvaluateAny impl for our expression type needs to reference all of these constraints. 63 | impl EvaluateAny for PairExpr 64 | where 65 | V: From + From<(V, V)> + std::ops::Add + ProjectPair, 66 | { 67 | fn evaluate(&self) -> V { 68 | self.0.evaluate() 69 | } 70 | } 71 | 72 | /// Now we need a value type that can be either an integer or a pair, with all of the various value 73 | /// impls that we've defined or used so far. 74 | #[derive(Debug, PartialEq)] 75 | pub enum IntOrPair { 76 | Int(i64), 77 | Pair(Box, Box), 78 | } 79 | 80 | impl From for IntOrPair { 81 | fn from(value: i64) -> IntOrPair { 82 | IntOrPair::Int(value) 83 | } 84 | } 85 | 86 | impl std::ops::Add for IntOrPair { 87 | type Output = Self; 88 | fn add(self, other: Self) -> Self { 89 | if let IntOrPair::Int(lhs) = self { 90 | if let IntOrPair::Int(rhs) = other { 91 | return IntOrPair::Int(lhs + rhs); 92 | } 93 | } 94 | panic!("Cannot add non-integers"); 95 | } 96 | } 97 | 98 | impl From<(IntOrPair, IntOrPair)> for IntOrPair { 99 | fn from(value: (IntOrPair, IntOrPair)) -> IntOrPair { 100 | IntOrPair::Pair(Box::new(value.0), Box::new(value.1)) 101 | } 102 | } 103 | 104 | impl ProjectPair for IntOrPair { 105 | fn first(self) -> IntOrPair { 106 | if let IntOrPair::Pair(first, _) = self { 107 | return *first; 108 | } 109 | panic!("Cannot project non-pairs"); 110 | } 111 | 112 | fn second(self) -> IntOrPair { 113 | if let IntOrPair::Pair(_, second) = self { 114 | return *second; 115 | } 116 | panic!("Cannot project non-pairs"); 117 | } 118 | } 119 | 120 | #[cfg(test)] 121 | mod tests { 122 | use super::*; 123 | use crate::ch04_smart_constructors::*; 124 | 125 | // We can still evaluate all of the expressions that don't mention pairs, even though pairs are 126 | // now possible! The names of the types are different, but otherwise these tests are exactly 127 | // the same as in ch03 and ch07b! 128 | 129 | #[test] 130 | fn can_evaluate_ugly_expression() { 131 | // 118 + 1219 132 | let add: PairExpr = add(integer_literal(118), integer_literal(1219)); 133 | // Kind of gross 134 | assert_eq!( 135 | (&add as &EvaluateAny).evaluate(), 136 | IntOrPair::Int(1337) 137 | ); 138 | // A little bit nicer 139 | assert_eq!(evaluate_any::(&add), IntOrPair::Int(1337)); 140 | } 141 | 142 | #[test] 143 | fn can_evaluate_nested_expression() { 144 | // 30000 + 1330 + 7 145 | let add: PairExpr = add( 146 | integer_literal(30000), 147 | add(integer_literal(1330), integer_literal(7)), 148 | ); 149 | assert_eq!( 150 | (&add as &EvaluateAny).evaluate(), 151 | IntOrPair::Int(31337) 152 | ); 153 | assert_eq!(evaluate_any::(&add), IntOrPair::Int(31337)); 154 | } 155 | 156 | // And we can also evaluate expressions that mention pairs. 157 | 158 | #[test] 159 | fn can_evaluate_pair() { 160 | let expr: PairExpr = pair(integer_literal(7), integer_literal(6)); 161 | assert_eq!( 162 | (&expr as &EvaluateAny).evaluate(), 163 | IntOrPair::Pair(Box::new(IntOrPair::Int(7)), Box::new(IntOrPair::Int(6))) 164 | ); 165 | assert_eq!( 166 | evaluate_any::(&expr), 167 | IntOrPair::Pair(Box::new(IntOrPair::Int(7)), Box::new(IntOrPair::Int(6))) 168 | ); 169 | } 170 | 171 | #[test] 172 | fn can_evaluate_pair_projection() { 173 | let expr: PairExpr = first(pair(integer_literal(7), integer_literal(6))); 174 | assert_eq!( 175 | (&expr as &EvaluateAny).evaluate(), 176 | IntOrPair::Int(7) 177 | ); 178 | assert_eq!(evaluate_any::(&expr), IntOrPair::Int(7)); 179 | } 180 | 181 | // But we'd better get the types right, or we'll panic! 182 | 183 | #[test] 184 | fn cannot_project_integer() { 185 | let expr: PairExpr = first(integer_literal(7)); 186 | let result = std::panic::catch_unwind(|| (&expr as &EvaluateAny).evaluate()); 187 | assert!(result.is_err()); 188 | } 189 | 190 | #[test] 191 | fn cannot_add_pairs() { 192 | let expr: PairExpr = add( 193 | pair(integer_literal(1), integer_literal(2)), 194 | integer_literal(3), 195 | ); 196 | let result = std::panic::catch_unwind(|| (&expr as &EvaluateAny).evaluate()); 197 | assert!(result.is_err()); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/ch07d_safer_pair_evaluation.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! The panics in our previous evaluation rules for pairs weren't that great. Let's create a safer 17 | //! value type that treats errors as their own kind of result. We won't need to change any of the 18 | //! evaluation rules, just the definition of our result type! 19 | 20 | use crate::ch07c_pair_evaluation::*; 21 | 22 | #[derive(Debug, PartialEq)] 23 | pub struct SafeIntOrPair(Option); 24 | 25 | impl From> for SafeIntOrPair { 26 | fn from(value: Option) -> SafeIntOrPair { 27 | SafeIntOrPair(value) 28 | } 29 | } 30 | 31 | impl From for SafeIntOrPair { 32 | fn from(value: i64) -> SafeIntOrPair { 33 | Some(IntOrPair::Int(value)).into() 34 | } 35 | } 36 | 37 | impl std::ops::Add for SafeIntOrPair { 38 | type Output = Self; 39 | fn add(self, other: Self) -> Self { 40 | if let SafeIntOrPair(Some(IntOrPair::Int(lhs))) = self { 41 | if let SafeIntOrPair(Some(IntOrPair::Int(rhs))) = other { 42 | return Some(IntOrPair::Int(lhs + rhs)).into(); 43 | } 44 | } 45 | None.into() 46 | } 47 | } 48 | 49 | impl From<(SafeIntOrPair, SafeIntOrPair)> for SafeIntOrPair { 50 | fn from(value: (SafeIntOrPair, SafeIntOrPair)) -> SafeIntOrPair { 51 | if let SafeIntOrPair(Some(first)) = value.0 { 52 | if let SafeIntOrPair(Some(second)) = value.1 { 53 | return Some(IntOrPair::Pair(Box::new(first), Box::new(second))).into(); 54 | } 55 | } 56 | None.into() 57 | } 58 | } 59 | 60 | impl ProjectPair for SafeIntOrPair { 61 | fn first(self) -> SafeIntOrPair { 62 | if let SafeIntOrPair(Some(IntOrPair::Pair(first, _))) = self { 63 | return SafeIntOrPair(Some(*first)); 64 | } 65 | None.into() 66 | } 67 | 68 | fn second(self) -> SafeIntOrPair { 69 | if let SafeIntOrPair(Some(IntOrPair::Pair(_, second))) = self { 70 | return SafeIntOrPair(Some(*second)); 71 | } 72 | None.into() 73 | } 74 | } 75 | 76 | #[cfg(test)] 77 | mod tests { 78 | use super::*; 79 | use crate::ch04_smart_constructors::*; 80 | use crate::ch07a_pairs::*; 81 | use crate::ch07b_generic_evaluation::*; 82 | 83 | // All of the successful evaluations look exactly the same, except that we're referencing the 84 | // new result type (SafeIntOrPair instead of IntOrPair). 85 | 86 | #[test] 87 | fn can_evaluate_ugly_expression() { 88 | // 118 + 1219 89 | let add: PairExpr = add(integer_literal(118), integer_literal(1219)); 90 | // Kind of gross 91 | assert_eq!( 92 | (&add as &EvaluateAny).evaluate(), 93 | Some(IntOrPair::Int(1337)).into() 94 | ); 95 | // A little bit nicer 96 | assert_eq!( 97 | evaluate_any::(&add), 98 | Some(IntOrPair::Int(1337)).into() 99 | ); 100 | } 101 | 102 | #[test] 103 | fn can_evaluate_nested_expression() { 104 | // 30000 + 1330 + 7 105 | let add: PairExpr = add( 106 | integer_literal(30000), 107 | add(integer_literal(1330), integer_literal(7)), 108 | ); 109 | assert_eq!( 110 | (&add as &EvaluateAny).evaluate(), 111 | Some(IntOrPair::Int(31337)).into() 112 | ); 113 | assert_eq!( 114 | evaluate_any::(&add), 115 | Some(IntOrPair::Int(31337)).into() 116 | ); 117 | } 118 | 119 | #[test] 120 | fn can_evaluate_pair() { 121 | let expr: PairExpr = pair(integer_literal(7), integer_literal(6)); 122 | assert_eq!( 123 | (&expr as &EvaluateAny).evaluate(), 124 | Some(IntOrPair::Pair( 125 | Box::new(IntOrPair::Int(7)), 126 | Box::new(IntOrPair::Int(6)) 127 | )) 128 | .into() 129 | ); 130 | assert_eq!( 131 | evaluate_any::(&expr), 132 | Some(IntOrPair::Pair( 133 | Box::new(IntOrPair::Int(7)), 134 | Box::new(IntOrPair::Int(6)) 135 | )) 136 | .into() 137 | ); 138 | } 139 | 140 | #[test] 141 | fn can_evaluate_pair_projection() { 142 | let expr: PairExpr = first(pair(integer_literal(7), integer_literal(6))); 143 | assert_eq!( 144 | (&expr as &EvaluateAny).evaluate(), 145 | Some(IntOrPair::Int(7)).into() 146 | ); 147 | assert_eq!( 148 | evaluate_any::(&expr), 149 | Some(IntOrPair::Int(7)).into() 150 | ); 151 | } 152 | 153 | // The failed evaluations now produce an Error value, instead of panicking! Nice! 154 | 155 | #[test] 156 | fn cannot_project_integer() { 157 | let expr: PairExpr = first(integer_literal(7)); 158 | assert_eq!( 159 | (&expr as &EvaluateAny).evaluate(), 160 | None.into() 161 | ); 162 | assert_eq!(evaluate_any::(&expr), None.into()); 163 | } 164 | 165 | #[test] 166 | fn cannot_add_pairs() { 167 | let expr: PairExpr = add( 168 | pair(integer_literal(1), integer_literal(2)), 169 | integer_literal(3), 170 | ); 171 | assert_eq!( 172 | (&expr as &EvaluateAny).evaluate(), 173 | None.into() 174 | ); 175 | assert_eq!(evaluate_any::(&expr), None.into()); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/ch08a_expressions.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! Let's create a Rust trait for the `Expr` Haskell typeclass from the papers. 17 | 18 | use crate::ch02_open_sum::*; 19 | use crate::ch05a_multiplication::*; 20 | use crate::ch07a_pairs::*; 21 | 22 | /// An Expression represents the AST of one of our mini-languages. It has a `Signature` associated 23 | /// type, which is a `Sum` of all of the possible terms in the language, along with methods for 24 | /// converting between the signature and the expression. This is the Rust equivalent of the `Expr` 25 | /// type family from the papers; it ties the knot on the recursive generic type parameters that 26 | /// allow expressions to contain subexpressions. 27 | pub trait Expression { 28 | type Signature; 29 | fn wrap(sig: Self::Signature) -> Self; 30 | fn unwrap(&self) -> &Self::Signature; 31 | } 32 | 33 | // And then we define an Expression impl for each of our actual expression AST types. They're all 34 | // *very* boilerplate. But! If we've done this right, it will eliminate *all* of the other 35 | // per-AST-type boilerplate! 36 | 37 | impl Expression for Expr { 38 | type Signature = Sig; 39 | fn wrap(sig: Self::Signature) -> Self { 40 | Self(Box::new(sig)) 41 | } 42 | fn unwrap(&self) -> &Self::Signature { 43 | &self.0 44 | } 45 | } 46 | 47 | impl Expression for MultExpr { 48 | type Signature = MultSig; 49 | fn wrap(sig: Self::Signature) -> Self { 50 | Self(Box::new(sig)) 51 | } 52 | fn unwrap(&self) -> &Self::Signature { 53 | &self.0 54 | } 55 | } 56 | 57 | impl Expression for NoAddExpr { 58 | type Signature = NoAddSig; 59 | fn wrap(sig: Self::Signature) -> Self { 60 | Self(Box::new(sig)) 61 | } 62 | fn unwrap(&self) -> &Self::Signature { 63 | &self.0 64 | } 65 | } 66 | 67 | impl Expression for PairExpr { 68 | type Signature = PairSig; 69 | fn wrap(sig: Self::Signature) -> Self { 70 | Self(Box::new(sig)) 71 | } 72 | fn unwrap(&self) -> &Self::Signature { 73 | &self.0 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/ch08b_open_recursion_evaluation.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | //! We have a lot of cool features from previous modules, but they require a lot of annoying 17 | //! boilerplate. Let's try to eliminate as much of that boilerplate as we can, using our new 18 | //! `Expression` trait. 19 | 20 | use crate::ch02_open_sum::*; 21 | use crate::ch05a_multiplication::*; 22 | use crate::ch07a_pairs::*; 23 | use crate::ch07c_pair_evaluation::*; 24 | use crate::ch08a_expressions::*; 25 | 26 | // Ideally we would be able to reuse EvaluateAny. It's quite nice! But as much as we want to, we 27 | // can't create a generic impl for any Expression. It would look like this: 28 | // 29 | // impl EvaluateAny for E 30 | // where E: Expression, E::Signature: EvaluateAny 31 | // { 32 | // fn evaluate(&self) -> V { 33 | // self.unwrap().evaluate() 34 | // } 35 | // } 36 | // 37 | // That will compile just fine, but the compiler will never actually **use** that impl, because the 38 | // bounds are recursive: to prove that Expr implements EvaluateAny, we have to show that 39 | // Expr::Signature (aka Sig) implements EvaluateAny. To do that, we have to (skipping a 40 | // couple of steps) show that Add implements EvaluateAny. And to do **that**, we have to 41 | // show that Expr implements EvaluateAny — which is what we're trying to prove in the first place. 42 | // 43 | // Doh! 44 | // 45 | // To get around this, we have to use *open recursion*. All of our EvaluateAny impls assume that 46 | // the subexpression type E also implements EvaluateAny, and calls the evaluate() method directly 47 | // to evaluate subexpressions before combining them together. (Look add Add's implementation 48 | // for a prime example.) Instead of doing that, our new eval() method is going to take in a 49 | // parameter the function that it should use to evaluate subexpressions! That means that the impls 50 | // don't have to depend on their subexpression types implementing the trait too — which ends up 51 | // breaking the recursive cycle that got us into trouble! 52 | // 53 | // (If you've read Swierstra's paper deeply, what we've done here is fuse together each term type's 54 | // `fmap` and `evalAlgebra` functions into a single `eval` method.) 55 | 56 | /// Each term type should implement this trait to define how it should be evaluated. If the term 57 | /// has any subexpressions, it should use `eval_subexpr` to evaluate them. 58 | pub trait Eval { 59 | fn eval(&self, eval_subexpr: F) -> V 60 | where 61 | F: FnMut(&E) -> V; 62 | } 63 | 64 | // This impls should all look very much like their EvaluateAny counterparts, but will all calls to 65 | // `foo.evaluate()` being replaced with `eval_subexpr(foo)`. 66 | 67 | impl Eval for IntegerLiteral 68 | where 69 | V: From, 70 | { 71 | fn eval(&self, _eval_subexpr: F) -> V 72 | where 73 | F: FnMut(&E) -> V, 74 | { 75 | V::from(self.value) 76 | } 77 | } 78 | 79 | impl Eval for Add 80 | where 81 | V: std::ops::Add, 82 | { 83 | fn eval(&self, mut eval_subexpr: F) -> V 84 | where 85 | F: FnMut(&E) -> V, 86 | { 87 | eval_subexpr(&self.lhs) + eval_subexpr(&self.rhs) 88 | } 89 | } 90 | 91 | impl Eval for Multiply 92 | where 93 | V: std::ops::Mul, 94 | { 95 | fn eval(&self, mut eval_subexpr: F) -> V 96 | where 97 | F: FnMut(&E) -> V, 98 | { 99 | eval_subexpr(&self.lhs) * eval_subexpr(&self.rhs) 100 | } 101 | } 102 | 103 | impl Eval for Pair 104 | where 105 | V: From<(V, V)>, 106 | { 107 | fn eval(&self, mut eval_subexpr: F) -> V 108 | where 109 | F: FnMut(&E) -> V, 110 | { 111 | V::from((eval_subexpr(&self.first), eval_subexpr(&self.second))) 112 | } 113 | } 114 | 115 | impl Eval for First 116 | where 117 | V: ProjectPair, 118 | { 119 | fn eval(&self, mut eval_subexpr: F) -> V 120 | where 121 | F: FnMut(&E) -> V, 122 | { 123 | eval_subexpr(&self.pair).first() 124 | } 125 | } 126 | 127 | impl Eval for Second 128 | where 129 | V: ProjectPair, 130 | { 131 | fn eval(&self, mut eval_subexpr: F) -> V 132 | where 133 | F: FnMut(&E) -> V, 134 | { 135 | eval_subexpr(&self.pair).second() 136 | } 137 | } 138 | 139 | impl Eval for Sum 140 | where 141 | L: Eval, 142 | R: Eval, 143 | { 144 | fn eval(&self, eval_subexpr: F) -> V 145 | where 146 | F: FnMut(&E) -> V, 147 | { 148 | match self { 149 | Sum::Left(lhs) => lhs.eval(eval_subexpr), 150 | Sum::Right(rhs) => rhs.eval(eval_subexpr), 151 | } 152 | } 153 | } 154 | 155 | // And now, because there are no horrible cycles in our impls anymore, we can define a generic Eval 156 | // impl for *any* Expression type! And even better, unlike our EvaluateAny impls, these don't need 157 | // to copy all of the constraints from the per-term impls! 158 | 159 | impl Eval for E 160 | where 161 | E: Expression, 162 | E::Signature: Eval, 163 | { 164 | fn eval(&self, eval_subexpr: F) -> V 165 | where 166 | F: FnMut(&E) -> V, 167 | { 168 | self.unwrap().eval(eval_subexpr) 169 | } 170 | } 171 | 172 | // One last bow to tie this together. We need something that can call these `eval` methods with 173 | // the right recursion function. The simplest version would look like this: 174 | // 175 | // pub fn evaluate(expr: &E) -> V where E: Eval { 176 | // e.eval(evaluate) 177 | // } 178 | // 179 | // and you'd call it like this: 180 | // 181 | // let expr: Expr = /* whatever */; 182 | // evaluate::(&expr); 183 | // 184 | // Not bad, but we can do better. The following lets us call the following for *any* Expression 185 | // type: 186 | // 187 | // let expr: Expr = /* whatever */; 188 | // expr.evaluate::(); 189 | 190 | trait Evaluate: Sized { 191 | fn evaluate(&self) -> V 192 | where 193 | Self: Eval; 194 | } 195 | 196 | impl Evaluate for E 197 | where 198 | E: Sized, 199 | { 200 | fn evaluate(&self) -> V 201 | where 202 | Self: Eval, 203 | { 204 | self.eval(|e| e.evaluate()) 205 | } 206 | } 207 | 208 | #[cfg(test)] 209 | mod tests { 210 | use super::*; 211 | use crate::ch04_smart_constructors::*; 212 | 213 | // Hey look! All of the test cases work without modification (except for that we're calling 214 | // our new evaluate method)! 215 | 216 | #[test] 217 | fn can_evaluate_ugly_expression() { 218 | let add: Expr = add(integer_literal(118), integer_literal(1219)); 219 | assert_eq!(add.evaluate::(), 1337); 220 | } 221 | 222 | #[test] 223 | fn can_evaluate_nested_expression() { 224 | // 30000 + 1330 + 7 225 | let add: Expr = add( 226 | integer_literal(30000), 227 | add(integer_literal(1330), integer_literal(7)), 228 | ); 229 | assert_eq!(add.evaluate::(), 31337); 230 | } 231 | 232 | #[test] 233 | fn can_evaluate_multiplication() { 234 | let mult: MultExpr = add( 235 | multiply(integer_literal(80), integer_literal(5)), 236 | integer_literal(4), 237 | ); 238 | assert_eq!(mult.evaluate::(), 404); 239 | } 240 | 241 | #[test] 242 | fn can_evaluate_no_add_multiplication() { 243 | let mult: NoAddExpr = multiply(integer_literal(6), integer_literal(7)); 244 | assert_eq!(mult.evaluate::(), 42); 245 | } 246 | 247 | #[test] 248 | fn can_evaluate_pair() { 249 | let expr: PairExpr = pair(integer_literal(7), integer_literal(6)); 250 | assert_eq!( 251 | expr.evaluate::(), 252 | IntOrPair::Pair(Box::new(IntOrPair::Int(7)), Box::new(IntOrPair::Int(6))) 253 | ); 254 | } 255 | 256 | #[test] 257 | fn can_evaluate_pair_projection() { 258 | let expr: PairExpr = first(pair(integer_literal(7), integer_literal(6))); 259 | assert_eq!(expr.evaluate::(), IntOrPair::Int(7)); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2018-2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | #![feature(optin_builtin_traits)] 17 | 18 | pub mod ch01a_before; 19 | pub mod ch01b_new_method; 20 | pub mod ch01c_sad_face; 21 | 22 | pub mod ch02_open_sum; 23 | pub mod ch03_evaluation; 24 | pub mod ch04_smart_constructors; 25 | 26 | pub mod ch05a_multiplication; 27 | pub mod ch05b_display; 28 | 29 | pub mod ch06_calculator_monad; 30 | 31 | pub mod ch07a_pairs; 32 | pub mod ch07b_generic_evaluation; 33 | pub mod ch07c_pair_evaluation; 34 | pub mod ch07d_safer_pair_evaluation; 35 | 36 | pub mod ch08a_expressions; 37 | pub mod ch08b_open_recursion_evaluation; 38 | 39 | pub mod old; 40 | -------------------------------------------------------------------------------- /src/old.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2018-2019, Douglas Creager. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the 11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | // express or implied. See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | // ------------------------------------------------------------------------------------------------ 17 | // Data types 18 | 19 | pub auto trait NotEq {} 20 | impl !NotEq for (X, X) {} 21 | 22 | pub struct IntegerLiteral { 23 | pub value: i64, 24 | } 25 | 26 | pub fn integer_literal>(value: i64) -> E { 27 | E::from(IntegerLiteral { value }) 28 | } 29 | 30 | pub struct Add { 31 | pub lhs: Box, 32 | pub rhs: Box, 33 | } 34 | 35 | pub fn add>>(lhs: E, rhs: E) -> E { 36 | E::from(Add { 37 | lhs: Box::new(lhs), 38 | rhs: Box::new(rhs), 39 | }) 40 | } 41 | 42 | // ------------------------------------------------------------------------------------------------ 43 | // Open sums 44 | 45 | pub enum CoproductPair { 46 | Left(L), 47 | Right(R), 48 | } 49 | 50 | impl CoproductPair { 51 | pub fn new_left(left: L) -> CoproductPair { 52 | CoproductPair::Left(left) 53 | } 54 | pub fn new_right(right: R) -> CoproductPair { 55 | CoproductPair::Right(right) 56 | } 57 | } 58 | 59 | impl From for CoproductPair { 60 | fn from(left: L) -> CoproductPair { 61 | CoproductPair::Left(left) 62 | } 63 | } 64 | 65 | impl From for CoproductPair 66 | where 67 | R: From, 68 | (X, L): NotEq, 69 | (X, Self): NotEq, 70 | { 71 | fn from(x: X) -> CoproductPair { 72 | CoproductPair::Right(R::from(x)) 73 | } 74 | } 75 | 76 | macro_rules! Coproduct { 77 | { $A:ty, $B:ty } => { CoproductPair<$A, $B> }; 78 | { $A:ty, $($B:ty),+ } => { CoproductPair<$A, Coproduct![$($B),+]> }; 79 | } 80 | 81 | // ------------------------------------------------------------------------------------------------ 82 | // Evaluate 83 | 84 | pub trait Result: From + std::ops::Add {} 85 | impl Result for i64 {} 86 | 87 | pub trait Evaluate { 88 | fn evaluate(&self) -> V; 89 | } 90 | 91 | impl Evaluate for CoproductPair 92 | where 93 | L: Evaluate, 94 | R: Evaluate, 95 | { 96 | fn evaluate(&self) -> V { 97 | match self { 98 | CoproductPair::Left(l) => l.evaluate(), 99 | CoproductPair::Right(r) => r.evaluate(), 100 | } 101 | } 102 | } 103 | 104 | impl Evaluate for IntegerLiteral { 105 | fn evaluate(&self) -> V { 106 | V::from(self.value) 107 | } 108 | } 109 | 110 | impl Evaluate for Add 111 | where 112 | E: Evaluate, 113 | { 114 | fn evaluate(&self) -> V { 115 | self.lhs.evaluate::() + self.rhs.evaluate::() 116 | } 117 | } 118 | 119 | // ------------------------------------------------------------------------------------------------ 120 | // Expr 121 | 122 | pub type Sig = Coproduct![IntegerLiteral, Add]; 123 | pub struct Expr(Sig); 124 | 125 | impl From for Expr 126 | where 127 | Sig: From, 128 | { 129 | fn from(x: X) -> Expr { 130 | Expr(Sig::::from(x)) 131 | } 132 | } 133 | 134 | impl Evaluate for Expr { 135 | fn evaluate(&self) -> V { 136 | self.0.evaluate() 137 | } 138 | } 139 | 140 | // ------------------------------------------------------------------------------------------------ 141 | // Desugaring 142 | 143 | /* 144 | pub struct Subtract { 145 | pub lhs: Box, 146 | pub rhs: Box, 147 | } 148 | 149 | impl Subtract { 150 | pub fn new(lhs: L, rhs: R) -> Subtract { 151 | Subtract { lhs: Box::new(lhs), rhs: Box::new(rhs) } 152 | } 153 | } 154 | 155 | impl Evaluate for Subtract where L: Evaluate, R: Evaluate 156 | { 157 | fn evaluate(&self) -> i64 { 158 | self.lhs.evaluate() - self.rhs.evaluate() 159 | } 160 | } 161 | */ 162 | 163 | // ------------------------------------------------------------------------------------------------ 164 | // Evaluate 165 | 166 | #[cfg(test)] 167 | mod eval_tests { 168 | use super::*; 169 | 170 | #[test] 171 | fn can_evaluate_integer_literal() { 172 | let one = IntegerLiteral { value: 1 }; 173 | assert_eq!(one.evaluate::(), 1); 174 | } 175 | 176 | #[test] 177 | fn can_evaluate_add() { 178 | let one = IntegerLiteral { value: 1 }; 179 | let two = IntegerLiteral { value: 2 }; 180 | let add = Add { 181 | lhs: Box::new(one), 182 | rhs: Box::new(two), 183 | }; 184 | assert_eq!(add.evaluate::(), 3); 185 | } 186 | 187 | /* 188 | #[test] 189 | fn can_evaluate_add3() { 190 | let one = IntegerLiteral { value: 1 }; 191 | let two = IntegerLiteral { value: 2 }; 192 | let three = IntegerLiteral { value: 3 }; 193 | let add = Add { 194 | lhs: Box::new(one), 195 | rhs: Box::new(Add { 196 | lhs: Box::new(two), 197 | rhs: Box::new(three), 198 | }), 199 | }; 200 | assert_eq!(add.evaluate::(), 6); 201 | } 202 | */ 203 | 204 | #[test] 205 | fn can_evaluate_expr_integer_literal() { 206 | let one: Expr = integer_literal(1); 207 | assert_eq!(one.evaluate::(), 1); 208 | } 209 | 210 | #[test] 211 | fn can_evaluate_expr_add() { 212 | let add: Expr = add(integer_literal(1), integer_literal(2)); 213 | assert_eq!(add.evaluate::(), 3); 214 | } 215 | 216 | #[test] 217 | fn can_evaluate_expr_add3() { 218 | let add: Expr = add( 219 | integer_literal(1), 220 | add(integer_literal(2), integer_literal(3)), 221 | ); 222 | assert_eq!(add.evaluate::(), 6); 223 | } 224 | 225 | /* 226 | #[test] 227 | fn can_evaluate_subtract() { 228 | let one = new_integer_literal(1); 229 | let two = new_integer_literal(2); 230 | let sub = Subtract::new(two, one); 231 | assert_eq!(evaluate(sub), 1); 232 | } 233 | */ 234 | } 235 | --------------------------------------------------------------------------------