Re: Some feedback on coding style
Oops. That is what I meant, but I don't know why I thought pre- and post-conditions get stored in metadata. It would be handy for :pre. Thanks, Ambrose. To the OP, I would still recommend using metadata to store the applicability test and also making it a precondition of the fn in a partial function implementation, but that will require more effort than I thought. On May 27, 2013 8:12 AM, "Ambrose Bonnaire-Sergeant" < abonnaireserge...@gmail.com> wrote: > Hi John, > > By :pre, do you mean function preconditions? eg. (fn [] {:pre [..]}) ? > > How is :pre related to metadata and dispatch? AFAICT it's purely for > macroexpansion and > there is no metadata available on the precondition post-macroexpansion. > > Thanks, > Ambrose > > On Mon, May 27, 2013 at 9:00 PM, John D. Hume wrote: > >> On May 26, 2013 8:53 PM, "Mark Engelberg" >> wrote: >> > >> > Another possible design choice is to store a domain-testing predicate >> in the function's metadata. >> >> Using metadata would be a much more idiomatic choice than using arity. >> Multiple arities are idiomatically used (like method overloading) to >> default arguments. >> >> An interesting detail James thought of but didn't call out is putting the >> domain check in the :pre metadata of the fn. This is an infrequently used >> Clojure feature, but it's a perfect fit for partial functions. Calling a fn >> with an argument that fails the :pre check will throw an exception, so the >> body can be written to assume it's got good input. Anyone can grab the :pre >> metadata to test for applicability, and anyone who knows clojure will have >> at least passing familiarity with the concept. >> >> Your macro can still pull apart the pattern matching forms to build the >> :pre fn, and it's still easy to do orElse-style composition. >> >> Unrelated: If you haven't already, you may want to read and follow up on >> the May 16 thread "core.match" before going very far with it. >> >> -- >> -- >> You received this message because you are subscribed to the Google >> Groups "Clojure" group. >> To post to this group, send email to clojure@googlegroups.com >> Note that posts from new members are moderated - please be patient with >> your first post. >> To unsubscribe from this group, send email to >> clojure+unsubscr...@googlegroups.com >> For more options, visit this group at >> http://groups.google.com/group/clojure?hl=en >> --- >> You received this message because you are subscribed to the Google Groups >> "Clojure" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to clojure+unsubscr...@googlegroups.com. >> For more options, visit https://groups.google.com/groups/opt_out. >> >> >> > > -- > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/groups/opt_out. > > > -- -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Some feedback on coding style
Hi John, By :pre, do you mean function preconditions? eg. (fn [] {:pre [..]}) ? How is :pre related to metadata and dispatch? AFAICT it's purely for macroexpansion and there is no metadata available on the precondition post-macroexpansion. Thanks, Ambrose On Mon, May 27, 2013 at 9:00 PM, John D. Hume wrote: > On May 26, 2013 8:53 PM, "Mark Engelberg" > wrote: > > > > Another possible design choice is to store a domain-testing predicate in > the function's metadata. > > Using metadata would be a much more idiomatic choice than using arity. > Multiple arities are idiomatically used (like method overloading) to > default arguments. > > An interesting detail James thought of but didn't call out is putting the > domain check in the :pre metadata of the fn. This is an infrequently used > Clojure feature, but it's a perfect fit for partial functions. Calling a fn > with an argument that fails the :pre check will throw an exception, so the > body can be written to assume it's got good input. Anyone can grab the :pre > metadata to test for applicability, and anyone who knows clojure will have > at least passing familiarity with the concept. > > Your macro can still pull apart the pattern matching forms to build the > :pre fn, and it's still easy to do orElse-style composition. > > Unrelated: If you haven't already, you may want to read and follow up on > the May 16 thread "core.match" before going very far with it. > > -- > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/groups/opt_out. > > > -- -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Some feedback on coding style
On May 26, 2013 8:53 PM, "Mark Engelberg" wrote: > > Another possible design choice is to store a domain-testing predicate in the function's metadata. Using metadata would be a much more idiomatic choice than using arity. Multiple arities are idiomatically used (like method overloading) to default arguments. An interesting detail James thought of but didn't call out is putting the domain check in the :pre metadata of the fn. This is an infrequently used Clojure feature, but it's a perfect fit for partial functions. Calling a fn with an argument that fails the :pre check will throw an exception, so the body can be written to assume it's got good input. Anyone can grab the :pre metadata to test for applicability, and anyone who knows clojure will have at least passing familiarity with the concept. Your macro can still pull apart the pattern matching forms to build the :pre fn, and it's still easy to do orElse-style composition. Unrelated: If you haven't already, you may want to read and follow up on the May 16 thread "core.match" before going very far with it. -- -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Some feedback on coding style
Another possible design choice is to store a domain-testing predicate in the function's metadata. (with-meta (fn [x] ...) {:domain integer?}) (defn is-defined? [f x] (-> f meta :domain x)) -- -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Some feedback on coding style
Thanks! On Sun, May 26, 2013 at 6:27 PM, Ambrose Bonnaire-Sergeant < abonnaireserge...@gmail.com> wrote: > Hi David, > > Clojure can generate auto-syms with a trailing #. > > user=> `(fn [x#] x#) > (clojure.core/fn [x__349__auto__] x__349__auto__) > > Thanks, > Ambrose > > > On Mon, May 27, 2013 at 9:08 AM, David Pollak < > feeder.of.the.be...@gmail.com> wrote: > >> Mark and James, >> >> Thank you for your input. >> >> There are two reasons why I don't want to simply test for nil as the >> result of running the pattern match: >> >> - The right side of the pattern can be side-effecting. For example, if >> you are servicing a web request, there may be database calls, etc. >> Therefore, I need a test that does not have side effects. >> - There may be a choice of multiple partial functions where the best >> choice is chosen (e.g., 3 different partial functions can serve a web >> request, but given the response type weighting, we may want to choose the >> JSON response, but we don't know which response type to choose until we've >> checked what the possibilities are) >> >> Anyway, using the joys of arity, I've solved the problem. >> >> (pf 33) ;; apply the partial function to 33 >> >> (pf :defined? 33) ;; is the function defined at 33 >> >> I blogged about the design choices at >> http://blog.goodstuff.im/first_clojure_macro >> >> Thanks! >> >> David >> >> >> >> On Sat, May 25, 2013 at 11:26 AM, James Reeves wrote: >> >>> In Scala, PartialFunction is a trait, which in Clojure I'd represent >>> using a protocol: >>> >>> (defprotocol Partial >>> (defined-at? [x])) >>> >>> (defn partial-fn [guard f] >>> (reify >>> Partial >>> (defined-at? [x] (guard x)) >>> clojure.lang.IFn >>> (invoke [f x] >>> {:pre [(guard x)]} >>> (f x >>> >>> And then, when I have a mechanism to create a partial function, I'd then >>> work on a macro to transform: >>> >>> (pfn [x :guard even?] (/ x 2)) >>> >>> Into: >>> >>> (partial-fn even? (fn [x] (/ x 2))) >>> >>> I'm not sure how much benefit you'd get out of partial functions that >>> are not part of the core language. You'd need to write a fair bit of >>> infrastructure around them, but it might be worth it. >>> >>> In Clojure, it seems quite common to use functions that return nil to >>> achieve the same effect as partial functions, and there are several >>> functions and macros, like some-> and keep, that support that idiom. >>> >>> - James >>> >>> >>> >>> On 25 May 2013 17:14, David Pollak wrote: >>> Hello, This is my first post to this group. If my post or the tone of my post is not up to this communities standards, please give me feedback so that I can integrate with the community. I'm coming from Scala-land. In Scala, there's a PartialFunction: http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.PartialFunction The key take-away for PartialFunctions is "... is a unary function where the domain does not necessarily include all values of type A." The ability to test a PartialFunction to see if the domain includes a particular value is very helpful. pf.isDefinedAt(x) allows testing to see if the function is defined at a given value of x. But a PartialFunction is a subclass of Function, so PartialFunctions can be applied: pf(x) The Scala compiler will take a pattern and turn it into a PartialFunction: def pf: PartialFunction[String, Number] = { case "" => 0 // special case blank to zero case x if isInt(x) => x.toInt case x if isDouble(x) => x.toDouble case x if isBigInt(x) => asBigInt(x) } Another property of PartialFunction is they can be composed: pf = pf1 orElse pf2 orElse pf3 // pf isDefinedAt any place any of the partial functions are defined We use PartialFunctions extensively in Lift to allow choosing if a particular URL should be served by Lift, if it should be served by a particular REST handler, etc. For example, defining a REST route in Lift: serve { case "api" :: "user" :: AsLong(userId) :: _ GetJson _ => User.find(userId).map(_.toJson) } As I've been learning Clojure in preparation for a presentation at Strange Loop and as part of a new project I've been working on ( http://blog.goodstuff.im/plugh), I am looking to bring the best things in Lift into the Clojure code I write. Clojure's pattern matching stuff is pretty nifty. I especially like how you can extract values out of a Map (this is *so* much more powerful that Scala's pattern matching, even with unapply... but I digress). So, I wrote a macro (it's my first, so feedback on the style for the macro itself): (defmacro match-func [& body] `(fn [~'x] (match [~'x] ~@body))) This creates a function that is the applicatio
Re: Some feedback on coding style
Hi David, Clojure can generate auto-syms with a trailing #. user=> `(fn [x#] x#) (clojure.core/fn [x__349__auto__] x__349__auto__) Thanks, Ambrose On Mon, May 27, 2013 at 9:08 AM, David Pollak wrote: > Mark and James, > > Thank you for your input. > > There are two reasons why I don't want to simply test for nil as the > result of running the pattern match: > > - The right side of the pattern can be side-effecting. For example, if you > are servicing a web request, there may be database calls, etc. Therefore, I > need a test that does not have side effects. > - There may be a choice of multiple partial functions where the best > choice is chosen (e.g., 3 different partial functions can serve a web > request, but given the response type weighting, we may want to choose the > JSON response, but we don't know which response type to choose until we've > checked what the possibilities are) > > Anyway, using the joys of arity, I've solved the problem. > > (pf 33) ;; apply the partial function to 33 > > (pf :defined? 33) ;; is the function defined at 33 > > I blogged about the design choices at > http://blog.goodstuff.im/first_clojure_macro > > Thanks! > > David > > > > On Sat, May 25, 2013 at 11:26 AM, James Reeves wrote: > >> In Scala, PartialFunction is a trait, which in Clojure I'd represent >> using a protocol: >> >> (defprotocol Partial >> (defined-at? [x])) >> >> (defn partial-fn [guard f] >> (reify >> Partial >> (defined-at? [x] (guard x)) >> clojure.lang.IFn >> (invoke [f x] >> {:pre [(guard x)]} >> (f x >> >> And then, when I have a mechanism to create a partial function, I'd then >> work on a macro to transform: >> >> (pfn [x :guard even?] (/ x 2)) >> >> Into: >> >> (partial-fn even? (fn [x] (/ x 2))) >> >> I'm not sure how much benefit you'd get out of partial functions that are >> not part of the core language. You'd need to write a fair bit of >> infrastructure around them, but it might be worth it. >> >> In Clojure, it seems quite common to use functions that return nil to >> achieve the same effect as partial functions, and there are several >> functions and macros, like some-> and keep, that support that idiom. >> >> - James >> >> >> >> On 25 May 2013 17:14, David Pollak wrote: >> >>> Hello, >>> >>> This is my first post to this group. If my post or the tone of my post >>> is not up to this communities standards, please give me feedback so that I >>> can integrate with the community. >>> >>> I'm coming from Scala-land. >>> >>> In Scala, there's a PartialFunction: >>> http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.PartialFunction >>> >>> The key take-away for PartialFunctions is "... is a unary function where >>> the domain does not necessarily include all values of type A." >>> >>> The ability to test a PartialFunction to see if the domain includes a >>> particular value is very helpful. pf.isDefinedAt(x) allows testing to see >>> if the function is defined at a given value of x. >>> >>> But a PartialFunction is a subclass of Function, so PartialFunctions can >>> be applied: >>> >>> pf(x) >>> >>> The Scala compiler will take a pattern and turn it into a >>> PartialFunction: >>> >>> def pf: PartialFunction[String, Number] = >>> { >>> case "" => 0 // special case blank to zero >>> case x if isInt(x) => x.toInt >>> case x if isDouble(x) => x.toDouble >>> case x if isBigInt(x) => asBigInt(x) >>> } >>> >>> Another property of PartialFunction is they can be composed: >>> >>> pf = pf1 orElse pf2 orElse pf3 // pf isDefinedAt any place any of the >>> partial functions are defined >>> >>> We use PartialFunctions extensively in Lift to allow choosing if a >>> particular URL should be served by Lift, if it should be served by a >>> particular REST handler, etc. For example, defining a REST route in Lift: >>> >>> serve { >>> case "api" :: "user" :: AsLong(userId) :: _ GetJson _ => >>> User.find(userId).map(_.toJson) >>> } >>> >>> As I've been learning Clojure in preparation for a presentation at >>> Strange Loop and as part of a new project I've been working on ( >>> http://blog.goodstuff.im/plugh), I am looking to bring the best things >>> in Lift into the Clojure code I write. >>> >>> Clojure's pattern matching stuff is pretty nifty. I especially like how >>> you can extract values out of a Map (this is *so* much more powerful that >>> Scala's pattern matching, even with unapply... but I digress). >>> >>> So, I wrote a macro (it's my first, so feedback on the style for the >>> macro itself): >>> >>> (defmacro match-func [& body] `(fn [~'x] (match [~'x] ~@body))) >>> >>> This creates a function that is the application of the match to a >>> parameter, so: >>> >>> ((match-func [q :guard even?] (+ 1 q) [z] (* 7 z)) 33) >>> ;; 231 >>> >>> I am struggling with the right style for how to create something that's >>> both a function: >>> >>> (def my-test (match-func [q :guard even?] (+ 1 q) [z] (* 7 z))) >>> >>> (
Re: Some feedback on coding style
Mark and James, Thank you for your input. There are two reasons why I don't want to simply test for nil as the result of running the pattern match: - The right side of the pattern can be side-effecting. For example, if you are servicing a web request, there may be database calls, etc. Therefore, I need a test that does not have side effects. - There may be a choice of multiple partial functions where the best choice is chosen (e.g., 3 different partial functions can serve a web request, but given the response type weighting, we may want to choose the JSON response, but we don't know which response type to choose until we've checked what the possibilities are) Anyway, using the joys of arity, I've solved the problem. (pf 33) ;; apply the partial function to 33 (pf :defined? 33) ;; is the function defined at 33 I blogged about the design choices at http://blog.goodstuff.im/first_clojure_macro Thanks! David On Sat, May 25, 2013 at 11:26 AM, James Reeves wrote: > In Scala, PartialFunction is a trait, which in Clojure I'd represent using > a protocol: > > (defprotocol Partial > (defined-at? [x])) > > (defn partial-fn [guard f] > (reify > Partial > (defined-at? [x] (guard x)) > clojure.lang.IFn > (invoke [f x] > {:pre [(guard x)]} > (f x > > And then, when I have a mechanism to create a partial function, I'd then > work on a macro to transform: > > (pfn [x :guard even?] (/ x 2)) > > Into: > > (partial-fn even? (fn [x] (/ x 2))) > > I'm not sure how much benefit you'd get out of partial functions that are > not part of the core language. You'd need to write a fair bit of > infrastructure around them, but it might be worth it. > > In Clojure, it seems quite common to use functions that return nil to > achieve the same effect as partial functions, and there are several > functions and macros, like some-> and keep, that support that idiom. > > - James > > > > On 25 May 2013 17:14, David Pollak wrote: > >> Hello, >> >> This is my first post to this group. If my post or the tone of my post is >> not up to this communities standards, please give me feedback so that I can >> integrate with the community. >> >> I'm coming from Scala-land. >> >> In Scala, there's a PartialFunction: >> http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.PartialFunction >> >> The key take-away for PartialFunctions is "... is a unary function where >> the domain does not necessarily include all values of type A." >> >> The ability to test a PartialFunction to see if the domain includes a >> particular value is very helpful. pf.isDefinedAt(x) allows testing to see >> if the function is defined at a given value of x. >> >> But a PartialFunction is a subclass of Function, so PartialFunctions can >> be applied: >> >> pf(x) >> >> The Scala compiler will take a pattern and turn it into a PartialFunction: >> >> def pf: PartialFunction[String, Number] = >> { >> case "" => 0 // special case blank to zero >> case x if isInt(x) => x.toInt >> case x if isDouble(x) => x.toDouble >> case x if isBigInt(x) => asBigInt(x) >> } >> >> Another property of PartialFunction is they can be composed: >> >> pf = pf1 orElse pf2 orElse pf3 // pf isDefinedAt any place any of the >> partial functions are defined >> >> We use PartialFunctions extensively in Lift to allow choosing if a >> particular URL should be served by Lift, if it should be served by a >> particular REST handler, etc. For example, defining a REST route in Lift: >> >> serve { >> case "api" :: "user" :: AsLong(userId) :: _ GetJson _ => >> User.find(userId).map(_.toJson) >> } >> >> As I've been learning Clojure in preparation for a presentation at >> Strange Loop and as part of a new project I've been working on ( >> http://blog.goodstuff.im/plugh), I am looking to bring the best things >> in Lift into the Clojure code I write. >> >> Clojure's pattern matching stuff is pretty nifty. I especially like how >> you can extract values out of a Map (this is *so* much more powerful that >> Scala's pattern matching, even with unapply... but I digress). >> >> So, I wrote a macro (it's my first, so feedback on the style for the >> macro itself): >> >> (defmacro match-func [& body] `(fn [~'x] (match [~'x] ~@body))) >> >> This creates a function that is the application of the match to a >> parameter, so: >> >> ((match-func [q :guard even?] (+ 1 q) [z] (* 7 z)) 33) >> ;; 231 >> >> I am struggling with the right style for how to create something that's >> both a function: >> >> (def my-test (match-func [q :guard even?] (+ 1 q) [z] (* 7 z))) >> >> (my-test 33) >> >> And also something that can be tested for definition at a given value. >> >> Put another way, if Clojure had something that's both a function and a >> thing that could be asked if it's defined for a given input, how would one >> in Clojure apply that thing and also ask that thing if it was defined? >> >> Thanks for reading my long question and I look forw
Re: Some feedback on coding style
In Scala, PartialFunction is a trait, which in Clojure I'd represent using a protocol: (defprotocol Partial (defined-at? [x])) (defn partial-fn [guard f] (reify Partial (defined-at? [x] (guard x)) clojure.lang.IFn (invoke [f x] {:pre [(guard x)]} (f x And then, when I have a mechanism to create a partial function, I'd then work on a macro to transform: (pfn [x :guard even?] (/ x 2)) Into: (partial-fn even? (fn [x] (/ x 2))) I'm not sure how much benefit you'd get out of partial functions that are not part of the core language. You'd need to write a fair bit of infrastructure around them, but it might be worth it. In Clojure, it seems quite common to use functions that return nil to achieve the same effect as partial functions, and there are several functions and macros, like some-> and keep, that support that idiom. - James On 25 May 2013 17:14, David Pollak wrote: > Hello, > > This is my first post to this group. If my post or the tone of my post is > not up to this communities standards, please give me feedback so that I can > integrate with the community. > > I'm coming from Scala-land. > > In Scala, there's a PartialFunction: > http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.PartialFunction > > The key take-away for PartialFunctions is "... is a unary function where > the domain does not necessarily include all values of type A." > > The ability to test a PartialFunction to see if the domain includes a > particular value is very helpful. pf.isDefinedAt(x) allows testing to see > if the function is defined at a given value of x. > > But a PartialFunction is a subclass of Function, so PartialFunctions can > be applied: > > pf(x) > > The Scala compiler will take a pattern and turn it into a PartialFunction: > > def pf: PartialFunction[String, Number] = > { > case "" => 0 // special case blank to zero > case x if isInt(x) => x.toInt > case x if isDouble(x) => x.toDouble > case x if isBigInt(x) => asBigInt(x) > } > > Another property of PartialFunction is they can be composed: > > pf = pf1 orElse pf2 orElse pf3 // pf isDefinedAt any place any of the > partial functions are defined > > We use PartialFunctions extensively in Lift to allow choosing if a > particular URL should be served by Lift, if it should be served by a > particular REST handler, etc. For example, defining a REST route in Lift: > > serve { > case "api" :: "user" :: AsLong(userId) :: _ GetJson _ => > User.find(userId).map(_.toJson) > } > > As I've been learning Clojure in preparation for a presentation at Strange > Loop and as part of a new project I've been working on ( > http://blog.goodstuff.im/plugh), I am looking to bring the best things in > Lift into the Clojure code I write. > > Clojure's pattern matching stuff is pretty nifty. I especially like how > you can extract values out of a Map (this is *so* much more powerful that > Scala's pattern matching, even with unapply... but I digress). > > So, I wrote a macro (it's my first, so feedback on the style for the macro > itself): > > (defmacro match-func [& body] `(fn [~'x] (match [~'x] ~@body))) > > This creates a function that is the application of the match to a > parameter, so: > > ((match-func [q :guard even?] (+ 1 q) [z] (* 7 z)) 33) > ;; 231 > > I am struggling with the right style for how to create something that's > both a function: > > (def my-test (match-func [q :guard even?] (+ 1 q) [z] (* 7 z))) > > (my-test 33) > > And also something that can be tested for definition at a given value. > > Put another way, if Clojure had something that's both a function and a > thing that could be asked if it's defined for a given input, how would one > in Clojure apply that thing and also ask that thing if it was defined? > > Thanks for reading my long question and I look forward to feedback. > > David > > > -- > Telegram, Simply Beautiful CMS https://telegr.am > Lift, the simply functional web framework http://liftweb.net > Follow me: http://twitter.com/dpp > Blog: http://goodstuff.im > > -- > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/groups/opt_out. > > > -- -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that post
Re: Some feedback on coding style
The most common way to do this in Clojure is to define your function such that if the input is not in the domain, the function returns nil. Since nil and false are the only "falsey" values in Clojure, you can use ordinary tests to determine if the function returned a result. The idiom that lets you apply a function and guarantee that there is a result is: (when-let [result (pf x)] ...) which is shorthand for: (let [result (pf x)] (when result ...)) If the when test fails, then the expression returns nil, which means that you naturally get another partial function restricted to the appropriate domain. There is a threading form, some->, which provides for certain kinds of partial function composition (a chain of calls, but as soon as you hit a nil result, the whole thing becomes nil). I can't think of anything exactly like orElse, but it's easy enough to write, since or returns the first non-falsey value: (defn orElse [pf1 pf2] (fn [x] (or (pf1 x) (pf2 x Extending this to a variable number of arguments is left as an exercise. As you can see, Clojure's punning of nil as both "no result" and a false value, along with the way that all other values are treated as true and the when idiom, allows for some compact code. However, it's not all roses. If you want your function to be able to return nil as an actual meaningful result, this strategy won't work. If your function sometimes returns false, then the when idiom fails. It works well enough in local contexts where you really understand the kind of data you're working with and know that nil and false won't be an issue, but if you're building a larger library, this approach is fragile. I've found that this lack of precision surrounding nil and false is one of the biggest source of bugs. You'll have code that works on all tests and "reasonable" input, but when someone uses your code on something that has nil or false in a collection and boom, things get squirrely. Since you're coming from Scala which carefully protects you from this kind of confusion, you'll probably find this to be a real annoyance. Nevertheless, most Clojurians just create partial functions in this way where a nil result means the function is undefined for that input, and muddle through okay. If partial functions play a central role in your library, then to be truly robust, you'll have to work harder than the typical, sloppy approach. Use maps as a role model, since maps are essentially partial functions. (get {:a 1, :b 2} :c) returns nil. But for special purposes, people can do (get {:a 1, :b 2} :c ::undefined), choosing a distinctive value to represent undefined. Try modeling your partial functions as functions that typically return nil if undefined, but an optional return value can be provided for undefined inputs. In general, the Clojure philosophy is that no guarantees are made if you pass an invalid input to a function: you might get garbage out, you might get nil, or you might get an error. It's not really consistent and different functions and libraries work in different ways. The nil-returning ones are the most easily composable, though, which is why that tends to be the most common way. -- -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.