From trevor.mcdonell at gmail.com Tue Sep 1 11:50:04 2020 From: trevor.mcdonell at gmail.com (Trevor McDonell) Date: Tue, 1 Sep 2020 13:50:04 +0200 Subject: [Haskell-cafe] [ANN] Accelerate v1.3 Message-ID: Hi all, On behalf of the Accelerate team, I'm happy to announce the release of version 1.3 of Accelerate: http://hackage.haskell.org/package/accelerate-1.3.0.0 This release includes many quality-of-life improvements for defining and using your own data types in embedded code. A summary of these new features is included in this release announcement. Generic Instances of Elt and Arrays are now derivable via Generic for simple (non-recursive Haskell'98) data types: data Point = Point Float Float Float deriving (Generic, Elt) At the scalar level we now also support *sum* data types: data Object = Sphere Point Float | Triangle Point Point Point deriving (Generic, Elt) Pattern synonyms Pattern synonyms for creating and accessing data types can now be defined and used, which in practice makes GHC's type checker far less angry than the old method of lift and unlift (which still exists). Pattern synonyms are used in the same way as you would a regular data constructor. Included are synonyms for tuples up to 16-tuples (T2, T3...) as well as indices (Z_, ::., I1, I2...) and other standard data types (Just_, True_, etc.) Defining your own pattern synonyms for product types can be done via the overloaded Pattern synonym at both the scalar: pattern Point_ :: Exp Float -> Exp Float -> Exp Float -> Exp Point pattern Point_ x y z = Pattern (x, y, z) ...and array levels: data State = State (Vector Point) (Vector Float) deriving (Generic, Arrays) pattern State_ :: Acc (Vector Point) -> Acc (Vector Float) -> Acc State pattern State_ { positions, masses } = Pattern (positions, masses) Note the syntax of the last example, which also generates the two accessor functions positions :: Acc State -> Acc (Vector Point) and masses :: Acc State -> Acc (Vector Float). This syntax is of course also available for use in Exp patterns. Defining pattern synonyms can also be achieved with the following TemplateHaskell splice (which is required for sum data types): mkPattern ''Object Embedded pattern matching This release also introduces support for *embedded pattern matching* via the new match operator, which allows us to reuse Haskell's case syntax in embedded code: intersect :: Exp Ray -> Exp Object -> Exp Bool intersect ray = match \case Sphere_ c r -> ... Triangle_ a b c -> ... Packages This release consists of the following packages: - accelerate-1.3.0.0 - accelerate-llvm-1.3.0.0 - accelerate-llvm-native-1.3.0.0 - accelerate-llvm-ptx-1.3.0.0 - accelerate-fft-1.3.0.0 - accelerate-examples-1.3.0.0 - accelerate-blas-0.3.0.0 - accelerate-bignum-0.3.0.0 - accelerate-io-1.3.0.0 - accelerate-io-array-0.1.0.0 - accelerate-io-bmp-0.1.0.0 - accelerate-io-bytestring-0.1.0.0 - accelerate-io-cereal-0.1.0.0 - accelerate-io-JuicyPixels-0.1.0.0 - accelerate-io-repa-0.1.0.0 - accelerate-io-vector-0.1.0.0 - colour-accelerate-0.4.0.0 - containers-accelerate-0.1.0.0 - hashable-accelerate-0.1.0.0 - gloss-accelerate-2.1.0.0 - gloss-raster-accelerate-2.1.0.0 - lens-accelerate-0.3.0.0 - linear-accelerate-0.7.0.0 - mwc-random-accelerate-0.2.0.0 - cuda-0.10.2.0 - cufft-0.10.0.0 - cublas-0.6.0.0 - cusparse-0.3.0.0 - cusolver-0.3.0.0 - nvvm-0.10.0.0 Contributors Special thanks to those who contributed to this release: - Trevor L. McDonell (@tmcdonell ) - Joshua Meredith (@JoshMeredith ) - Ivo Gabe de Wolff (@ivogabe ) - David van Balen (@dpvanbalen ) - Jaro Reinders (@noughtmare ) - Alex Lang (@alang9 ) - Paul Wilson (@statusfailed ) - @lennonhill - Travis Whitaker (@TravisWhitaker ) - Roger Bosman (@rogerbosman ) - Robbert van der Helm (@robbert-vdh ) - Sam (@sam-340453 ) - Lars van den Haak (@sakehl ) - Rinat Striungis (@Haskell-mouse ) - Viktor Kronvall (@considerate ) - Tom Smeding (@tomsmeding ) - Ryan Scott (@RyanGlScott ) -------------- next part -------------- An HTML attachment was scrubbed... URL: From 78emil at gmail.com Tue Sep 1 13:39:31 2020 From: 78emil at gmail.com (Emil Axelsson) Date: Tue, 1 Sep 2020 15:39:31 +0200 Subject: [Haskell-cafe] [ANN] Accelerate v1.3 In-Reply-To: References: Message-ID: Nice! How is that done? / Emil Den 2020-09-01 kl. 13:50, skrev Trevor McDonell: > > > Embedded pattern matching > > This release also introduces support for /embedded pattern > matching/ via the new |match| operator, which allows us to reuse > Haskell's case syntax in embedded code: > > intersect :: Exp Ray -> Exp Object -> Exp Bool > intersect ray= match\case > Sphere_ c r-> ... > Triangle_ a b c-> ... -------------- next part -------------- An HTML attachment was scrubbed... URL: From trevor.mcdonell at gmail.com Tue Sep 1 15:12:31 2020 From: trevor.mcdonell at gmail.com (Trevor McDonell) Date: Tue, 1 Sep 2020 17:12:31 +0200 Subject: [Haskell-cafe] [ANN] Accelerate v1.3 In-Reply-To: References: Message-ID: Good question! The trick is that `match` is passed an (n-ary) Haskell function on embedded terms (in the example hidden as a lambda-case) which it can continually re-apply, forcing every case alternative to succeed in turn so that we can explore the right-hand-side of each equation. This is necessary because we have staged compilation; the Haskell case statement is being resolved at an earlier stage (and possibly on a different device) than when we find out which branch actually succeeds. I think it's an interesting use case for explicitly bi-directional pattern synonyms which I haven't seen before. -T On Tue, 1 Sep 2020 at 15:39, Emil Axelsson <78emil at gmail.com> wrote: > Nice! How is that done? > > / Emil > > Den 2020-09-01 kl. 13:50, skrev Trevor McDonell: > > Embedded pattern matching > > This release also introduces support for *embedded pattern matching* via > the new match operator, which allows us to reuse Haskell's case syntax in > embedded code: > > intersect :: Exp Ray -> Exp Object -> Exp Bool > intersect ray = match \case > Sphere_ c r -> ... > Triangle_ a b c -> ... > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Tue Sep 1 20:16:38 2020 From: david.feuer at gmail.com (David Feuer) Date: Tue, 1 Sep 2020 16:16:38 -0400 Subject: [Haskell-cafe] [ANN] compact-sequences-0.2.0.0 Message-ID: I am pleased to release the second version of the compact-sequences package, now with deques! Changes: * Add deques. * Change operator precedence. * Add a test suite. Thanks to David Himmelstrup for setting up the test and CI framework. * Clean up internals somewhat. * Add a proof of amortized bounds for the stack implementation. Thanks, Li-Yao Xia. There are still plenty of things to work on, and help is always welcome. David Feuer From rae at richarde.dev Tue Sep 1 21:18:40 2020 From: rae at richarde.dev (Richard Eisenberg) Date: Tue, 1 Sep 2020 21:18:40 +0000 Subject: [Haskell-cafe] Constraints as a moveable feast? In-Reply-To: References: Message-ID: <010f01744b894244-e56bf594-dd56-48c6-a306-275cb395cf8b-000000@us-east-2.amazonses.com> I see the core idea as a fairly straightforward generalization of the "Partial Type Constructors" work -- except that I wouldn't phrase any of this as "floating out". In a few places, you've expanded Point a as Num a => (a, a). But Point a is *not* the same thing as Num a => (a, a). Indeed, you didn't define it that way. Instead, we should understand Point a to expand to (a, a) only when Num a holds. So, the occurrence of Point a causes a Num a constraint to arise. These Num a constraints then get put into the type. No floating. Note that types like `(Num a => a) -> a` and `Num a => a -> a` (both very valid today) mean quite different things, just as `(a -> a) -> a` and `a -> a -> a` mean different things. I think by rephrasing this without floating, we avoid the trouble with existentials, etc., that you are worried about. Personally, I would tack this onto "Partial Type Constructors" instead of trying to make this happen independently, but the two features would go nicely together. Richard > On Aug 17, 2020, at 6:50 AM, Anthony Clayden wrote: > > There's the makings of a cute feature in a very old Haskell Language report. (With thanks to the 'Partial Type Constructors' 2019 paper for ferreting this out.) > > type Num a => Point a = (a, a) > > 1990 v1.0 Haskell report. Yes that's a type synonym with a constraint. I'll give their examples, though they don't really demonstrate the potential: > > origin :: Point Integer -- we get Num a => wanted then discharged for free > origin = (0, 0) -- although the appearance of zero wants Num a anyway > > scale :: (Num a) => a -> Point a -> Point a -- seems wrong to give explicit Num a > scale w (x,y) = (w*x, w*y) -- the above sig would be inferred anyway > > The feature disappeared without trace in the next year's Language report. > > I'm thinking we should be able to write > > scale :: a -> Point a -> Point a -- get Num a for free by: > scale :: a -> (Num a => (a, a)) -> (Num a => (a, a)) -- expand the synonym -- this sig currently illegal > scale :: Num a => a -> (a, a) -> (a, a) -- float the constraints out to the implicit forall > > So this isn't specific to type synonyms: in general allow prefixing constraints to any sub-term in a type; type inference floats them to the front of the signature. You can kinda get the effect today (so I don't think this is entirely whacky), by cunning use of dummies and term-level combinators: > > typePoint :: Num a => (a, a) > typePoint = undefined > > scale w xy = const (undefined `asTypeOf` xy `asTypeOf` typePoint) (w `asTypeOf` fst xy) > -- inferred scale :: Num a => a -> (a,a) -> (a,a) > > (Putting a dummy undefined on RHS to show there's nothing up my sleeves, because the actual expression would unify to the right type anyway.) > > You can get maybe cleaner code with PatternSignatures. But it's all workarounds in terms/expressions. I want to do the Right Thing and get it accepted as a stand-alone signature. > > A couple of caveats: > * Haskell 1990 didn't have MultiParam Type Classes; > * nor existential quant. > > So if a nested constraint mentions both an existential tyvar with narrow scope and a universally quant'd tyvar, can't float out that constraint, have to reject the sig. > > The constraint in `type Num a => Point a = ...` appearing to the left of the introduced type syn, is also reminiscent of DatatypeContexts, so we could similarly float out the constraints from those appearing within a sig(?) And this would be a way to put constraints on newtypes similarly(?) (Can't put constraints on those currently, because there's not really a data constructor to bind the class dictionary to. Haskell 1990 didn't have newtypes.) > > Or is there something deeply anti-System FC here? > > AntC > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From joshchia at gmail.com Wed Sep 2 04:19:32 2020 From: joshchia at gmail.com (=?UTF-8?B?4piCSm9zaCBDaGlhICjorJ3ku7vkuK0p?=) Date: Wed, 2 Sep 2020 12:19:32 +0800 Subject: [Haskell-cafe] [ANN] compact-sequences-0.2.0.0 In-Reply-To: References: Message-ID: Could you elaborate on what the package does? The description is "Stacks, queues, and deques that take n + O(log n) space at the cost of having amortized O(log n) time complexity for basic operations." But why is it a 'cost' to have O(log n) instead of O(n) cost for basic operations on list-like structures (such as insert and delete presumably)? So, I didn't understand what the package is for. Do you mean "take amortized O(log n) time for basic operations at the cost of n + O(log n) space"? Or is it something else I didn't imagine? On Wed, Sep 2, 2020 at 4:17 AM David Feuer wrote: > I am pleased to release the second version of the compact-sequences > package, now with deques! > > Changes: > * Add deques. > * Change operator precedence. > * Add a test suite. Thanks to David Himmelstrup for setting up the > test and CI framework. > * Clean up internals somewhat. > * Add a proof of amortized bounds for the stack implementation. > Thanks, Li-Yao Xia. > > There are still plenty of things to work on, and help is always welcome. > > David Feuer > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Wed Sep 2 04:21:36 2020 From: david.feuer at gmail.com (David Feuer) Date: Wed, 2 Sep 2020 00:21:36 -0400 Subject: [Haskell-cafe] [ANN] compact-sequences-0.2.0.0 In-Reply-To: References: Message-ID: Typical stacks, queues, and deques are designed to have O(1) operations. These ones experiment in a different direction. On Wed, Sep 2, 2020, 12:19 AM ☂Josh Chia (謝任中) wrote: > Could you elaborate on what the package does? The description is "Stacks, > queues, and deques that take n + O(log n) space at the cost of having > amortized O(log n) time complexity for basic operations." But why is it a > 'cost' to have O(log n) instead of O(n) cost for basic operations on > list-like structures (such as insert and delete presumably)? So, I didn't > understand what the package is for. > > Do you mean "take amortized O(log n) time for basic operations at the cost > of n + O(log n) space"? Or is it something else I didn't imagine? > > > > On Wed, Sep 2, 2020 at 4:17 AM David Feuer wrote: > >> I am pleased to release the second version of the compact-sequences >> package, now with deques! >> >> Changes: >> * Add deques. >> * Change operator precedence. >> * Add a test suite. Thanks to David Himmelstrup for setting up the >> test and CI framework. >> * Clean up internals somewhat. >> * Add a proof of amortized bounds for the stack implementation. >> Thanks, Li-Yao Xia. >> >> There are still plenty of things to work on, and help is always welcome. >> >> David Feuer >> _______________________________________________ >> Haskell-Cafe mailing list >> To (un)subscribe, modify options or view archives go to: >> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >> Only members subscribed via the mailman list are allowed to post. > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From joshchia at gmail.com Wed Sep 2 04:26:04 2020 From: joshchia at gmail.com (=?UTF-8?B?4piCSm9zaCBDaGlhICjorJ3ku7vkuK0p?=) Date: Wed, 2 Sep 2020 12:26:04 +0800 Subject: [Haskell-cafe] [ANN] compact-sequences-0.2.0.0 In-Reply-To: References: Message-ID: Isn't a stack O(1) time for push & pop and O(n) space? What is the tradeoff being made here, where both space and time complexity increased? Or, am I missing something about the meaning of "n + O(log)", where the 'n' is not inside an 'O'. I don't really know what "n + O(log)" means exactly. On Wed, Sep 2, 2020 at 12:21 PM David Feuer wrote: > Typical stacks, queues, and deques are designed to have O(1) operations. > These ones experiment in a different direction. > > On Wed, Sep 2, 2020, 12:19 AM ☂Josh Chia (謝任中) wrote: > >> Could you elaborate on what the package does? The description is "Stacks, >> queues, and deques that take n + O(log n) space at the cost of having >> amortized O(log n) time complexity for basic operations." But why is it a >> 'cost' to have O(log n) instead of O(n) cost for basic operations on >> list-like structures (such as insert and delete presumably)? So, I didn't >> understand what the package is for. >> >> Do you mean "take amortized O(log n) time for basic operations at the >> cost of n + O(log n) space"? Or is it something else I didn't imagine? >> >> >> >> On Wed, Sep 2, 2020 at 4:17 AM David Feuer wrote: >> >>> I am pleased to release the second version of the compact-sequences >>> package, now with deques! >>> >>> Changes: >>> * Add deques. >>> * Change operator precedence. >>> * Add a test suite. Thanks to David Himmelstrup for setting up the >>> test and CI framework. >>> * Clean up internals somewhat. >>> * Add a proof of amortized bounds for the stack implementation. >>> Thanks, Li-Yao Xia. >>> >>> There are still plenty of things to work on, and help is always welcome. >>> >>> David Feuer >>> _______________________________________________ >>> Haskell-Cafe mailing list >>> To (un)subscribe, modify options or view archives go to: >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >>> Only members subscribed via the mailman list are allowed to post. >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Wed Sep 2 04:55:00 2020 From: david.feuer at gmail.com (David Feuer) Date: Wed, 2 Sep 2020 00:55:00 -0400 Subject: [Haskell-cafe] [ANN] compact-sequences-0.2.0.0 In-Reply-To: References: Message-ID: Yes, the n not being inside the O is the point. These structures hold n pointers in n words, plus a logarithmic term. Something like a linked list or Data.Sequence will hold n pointers in around c*n words, where c>1. For a list, c=3. On Wed, Sep 2, 2020, 12:26 AM ☂Josh Chia (謝任中) wrote: > Isn't a stack O(1) time for push & pop and O(n) space? What is the > tradeoff being made here, where both space and time complexity increased? > > Or, am I missing something about the meaning of "n + O(log)", where the > 'n' is not inside an 'O'. I don't really know what "n + O(log)" means > exactly. > > On Wed, Sep 2, 2020 at 12:21 PM David Feuer wrote: > >> Typical stacks, queues, and deques are designed to have O(1) operations. >> These ones experiment in a different direction. >> >> On Wed, Sep 2, 2020, 12:19 AM ☂Josh Chia (謝任中) >> wrote: >> >>> Could you elaborate on what the package does? The description is >>> "Stacks, queues, and deques that take n + O(log n) space at the cost of >>> having amortized O(log n) time complexity for basic operations." But why is >>> it a 'cost' to have O(log n) instead of O(n) cost for basic operations on >>> list-like structures (such as insert and delete presumably)? So, I didn't >>> understand what the package is for. >>> >>> Do you mean "take amortized O(log n) time for basic operations at the >>> cost of n + O(log n) space"? Or is it something else I didn't imagine? >>> >>> >>> >>> On Wed, Sep 2, 2020 at 4:17 AM David Feuer >>> wrote: >>> >>>> I am pleased to release the second version of the compact-sequences >>>> package, now with deques! >>>> >>>> Changes: >>>> * Add deques. >>>> * Change operator precedence. >>>> * Add a test suite. Thanks to David Himmelstrup for setting up the >>>> test and CI framework. >>>> * Clean up internals somewhat. >>>> * Add a proof of amortized bounds for the stack implementation. >>>> Thanks, Li-Yao Xia. >>>> >>>> There are still plenty of things to work on, and help is always welcome. >>>> >>>> David Feuer >>>> _______________________________________________ >>>> Haskell-Cafe mailing list >>>> To (un)subscribe, modify options or view archives go to: >>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >>>> Only members subscribed via the mailman list are allowed to post. >>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From tikhon at jelv.is Wed Sep 2 05:31:19 2020 From: tikhon at jelv.is (Tikhon Jelvis) Date: Tue, 1 Sep 2020 22:31:19 -0700 Subject: [Haskell-cafe] [ANN] compact-sequences-0.2.0.0 In-Reply-To: References: Message-ID: That explanation makes sense. It's probably worth adding a couple of sentences to that effect to the package description. I haven't seen that notation before, so I wasn't sure what it meant before you explained it here. On Tue, Sep 1, 2020 at 9:57 PM David Feuer wrote: > Yes, the n not being inside the O is the point. These structures hold n > pointers in n words, plus a logarithmic term. Something like a linked list > or Data.Sequence will hold n pointers in around c*n words, where c>1. For a > list, c=3. > > On Wed, Sep 2, 2020, 12:26 AM ☂Josh Chia (謝任中) wrote: > >> Isn't a stack O(1) time for push & pop and O(n) space? What is the >> tradeoff being made here, where both space and time complexity increased? >> >> Or, am I missing something about the meaning of "n + O(log)", where the >> 'n' is not inside an 'O'. I don't really know what "n + O(log)" means >> exactly. >> >> On Wed, Sep 2, 2020 at 12:21 PM David Feuer >> wrote: >> >>> Typical stacks, queues, and deques are designed to have O(1) operations. >>> These ones experiment in a different direction. >>> >>> On Wed, Sep 2, 2020, 12:19 AM ☂Josh Chia (謝任中) >>> wrote: >>> >>>> Could you elaborate on what the package does? The description is >>>> "Stacks, queues, and deques that take n + O(log n) space at the cost of >>>> having amortized O(log n) time complexity for basic operations." But why is >>>> it a 'cost' to have O(log n) instead of O(n) cost for basic operations on >>>> list-like structures (such as insert and delete presumably)? So, I didn't >>>> understand what the package is for. >>>> >>>> Do you mean "take amortized O(log n) time for basic operations at the >>>> cost of n + O(log n) space"? Or is it something else I didn't imagine? >>>> >>>> >>>> >>>> On Wed, Sep 2, 2020 at 4:17 AM David Feuer >>>> wrote: >>>> >>>>> I am pleased to release the second version of the compact-sequences >>>>> package, now with deques! >>>>> >>>>> Changes: >>>>> * Add deques. >>>>> * Change operator precedence. >>>>> * Add a test suite. Thanks to David Himmelstrup for setting up the >>>>> test and CI framework. >>>>> * Clean up internals somewhat. >>>>> * Add a proof of amortized bounds for the stack implementation. >>>>> Thanks, Li-Yao Xia. >>>>> >>>>> There are still plenty of things to work on, and help is always >>>>> welcome. >>>>> >>>>> David Feuer >>>>> _______________________________________________ >>>>> Haskell-Cafe mailing list >>>>> To (un)subscribe, modify options or view archives go to: >>>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >>>>> Only members subscribed via the mailman list are allowed to post. >>>> >>>> _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From simon at joyful.com Wed Sep 2 19:40:29 2020 From: simon at joyful.com (Simon Michael) Date: Wed, 2 Sep 2020 12:40:29 -0700 Subject: [Haskell-cafe] ANN: hledger-1.19 Message-ID: <99E6F25B-88AB-46AA-A911-C61824B5F0E1@joyful.com> I'm pleased to announce hledger 1.19 ! hledger is a robust, cross-platform, plain text accounting tool, with command-line, terminal and web UIs. It is a modern and largely compatible reimplementation of Ledger CLI with many improvements. You can use it to track time, money, investments, cryptocurrencies, inventory and more. See: http://hledger.org http://plaintextaccounting.org HIGHLIGHTS: New aregister and codes commands, more powerful CSV conditional rules, new sql output format, consistently default to flat mode, better colour control, cashflow report customisable like the others, more number/date/regexp parsing/validation, more speed. See all changes at https://hledger.org/release-notes . Thanks to release contributors Stephen Morgan, Dmitry Astapov, Michael Sanders, Henning Thielemann, Martin Michlmayr, and Colin Woodbury. GETTING STARTED: Many install methods are described at http://hledger.org/download. Here are three ways to build from source: stack update; stack install --resolver=lts hledger-lib-1.19 hledger-1.19 hledger-ui-1.19 hledger-web-1.19 --silent or: cabal v2-update; cabal v2-install hledger-1.19 hledger-web-1.19 hledger-ui-1.19 or: curl -sO https://raw.githubusercontent.com/simonmichael/hledger/master/hledger-install/hledger-install.sh less hledger-install.sh bash hledger-install.sh While building, peruse https://hledger.org/start For help, see http://hledger.org#help-feedback and join us on Freenode (#hledger, http://irc.hledger.org) or via Matrix (#freenode_#hledger:matrix.org, http://riot.hledger.org). New and old users, contributors, sponsors, and all feedback are most welcome! Best, -Simon From anthony_clayden at clear.net.nz Thu Sep 3 02:18:41 2020 From: anthony_clayden at clear.net.nz (Anthony Clayden) Date: Thu, 3 Sep 2020 14:18:41 +1200 Subject: [Haskell-cafe] Constraints as a moveable feast? Message-ID: > on *Tue Sep 1 21:18:40 UTC 2020, Richard Eisenberg wrote:* > I see the core idea as a fairly straightforward generalization of the > "Partial Type Constructors" work -- except that I wouldn't phrase any of > this as "floating out". Thanks Richard, but no: I see this as exploring what was the idea in that brief example in the 1990 Report. (It's very vague, so we're all speculating.) The example there is a type synonym, which the PTC work doesn't consider. And putting the constraint syntactically before the type (synonym/data/new) name suggests to me it's 'outer'. GHC actually can express a bit what I mean by "floating out". I didn't realise that at the time of writing the O.P., so I'll rework the example. I'm correcting it because as you say: > In a few places, you've expanded Point a as Num a => (a, a). But Point a > is *not* the same thing as Num a => (a, a). Indeed, you didn't define it > that way. ... The library supplier writes: {#- LANGUAGE RankNTypes #-} type Num a => Point a = (a, a) -- proposed mkPoint :: a -> a -> Point a -- pretend to hide the constructor mkPoint x y = (x, y) -- no using numeric ops/no Num needed The compiler expands the synonym to mkPoint :: a -> a -> Num a => (a, a). Note no parens around the expansion, unlike my O.P. That expansion is not valid H98, but it is valid in GHC with RankNTypes. The docos say it's a Rank-1 type. GHC infers mkPoint :: Num a => a -> a -> (a, a) And that's why I call it "floating out". Longer example below. The idea is the library writer could later change the implementation to a datatype or a newtype, etc; or even add a further constraint. They all carry the constraint(s) 'outer'; the client code doesn't need to know about the change. > Instead, we should understand Point a to expand to (a, a) only when Num a holds. That might be a valid use case (especially for constrained classes/type families); but it's not the use case I have in mind. > So, the occurrence of Point a causes a Num a constraint to arise. > These Num a constraints then get put into the type. No floating. We all think the 1991 DatatypeContexts design is daft because it raises wanted constraints but doesn't "put" them anywhere, so the client-module programmer has to add them explicitly in signatures. To improve that, where/how should the compiler "put into the type"? The client writes scale :: a -> Point a -> Point a -- expand the synonyms; then float out and squish duplicates -- ===> scale :: a -> Num a => (a, a) -> Num a => (a, a) -- no parens added -- ===> scale :: Num a => a -> (a, a) -> (a, a) The 'no parens' step (different to my O.P.) is valid GHC with RankNTypes; the last step is as GHC infers today, with Num a floated out. (Or if you don't like that phrase: "canonicalises", "simplifies"?) > Note that types like `(Num a => a) -> a` and `Num a => a -> a` > (both very valid today) mean quite different things, Yes; although the first isn't valid H98/wasn't valid in 1990; and I'm struggling to think of a use case for it, noting there's no existential quant inside the parens. (It's not Rank-2, but is it plain Rank-1 or Rank-1-and-a-half?) Anyhoo that was an error on my part. > I think by rephrasing this without floating, we avoid the trouble with > existentials, etc., that you are worried about. I'm not sure on that, but we'll leave it for another day. > Personally, I would tack this onto "Partial Type Constructors" > instead of trying to make this happen independently, > but the two features would go nicely together. PTCs introduce a lot of heavy machinery; whereas I'm thinking of this as just-below-the-surface syntactic sugar that gets expanded before type inference. OTOH this doesn't on its own support `instance Functor Point`, treating the type synonym as an unapplied constructor -- which is what the Hughes 1999 paper is tackling. (I have an idea how that could go using this same syntactic expansion idea.) Note BTW, that I didn't add any constraints to the data constructors -- which is all the 1991 design does (and that not consistently). Constructing a `Point` doesn't need any Num ops/methods. But the type is supplying a wanted Num a 'outside', because any expression operating on a Point likely uses arithmetic. Constraints on data constructors (as with GADTs) I see as orthogonal: there will be use cases for one alone, the other alone, or both combined. AntC -------------- next part -------------- An HTML attachment was scrubbed... URL: From rae at richarde.dev Thu Sep 3 17:54:59 2020 From: rae at richarde.dev (Richard Eisenberg) Date: Thu, 3 Sep 2020 17:54:59 +0000 Subject: [Haskell-cafe] Constraints as a moveable feast? In-Reply-To: References: Message-ID: <010f0174551b7f85-bf6f68cc-41db-4d07-9327-143c527ce036-000000@us-east-2.amazonses.com> > On Sep 2, 2020, at 10:18 PM, Anthony Clayden wrote: > > The compiler expands the synonym to mkPoint :: a -> a -> Num a => (a, a). > Note no parens around the expansion, unlike my O.P. That expansion is not valid H98, but it is valid in GHC with RankNTypes. The docos say it's a Rank-1 type. GHC infers > > mkPoint :: Num a => a -> a -> (a, a) > > And that's why I call it "floating out". Yes, but what about > pointX :: Point a -> a > pointX (x, _) = x ? We don't want that type to expand to (Num a => (a, a)) -> a. > The client writes > > scale :: a -> Point a -> Point a > -- expand the synonyms; then float out and squish duplicates > -- ===> scale :: a -> Num a => (a, a) -> Num a => (a, a) -- no parens added > -- ===> scale :: Num a => a -> (a, a) -> (a, a) > The 'no parens' step (different to my O.P.) is valid GHC with RankNTypes; the last step is as GHC infers today, with Num a floated out. (Or if you don't like that phrase: "canonicalises", "simplifies"?) Ah. Now I see a bit more of what you're getting at. But I really don't know what it means to expand without adding parens. Expanding a type synonym is an operation on an abstract syntax tree (AST). We often write ASTs by just writing out concrete syntax, and this sometimes requires adding parens. Even so, we're really just operating with ASTs -- and I don't think your "expand without parens" operation is defined on ASTs. For example: > data a + b = Inl a | Inr b > foo :: Point a + Point b I can't imagine we want to expand this to > foo :: Num a => (a, a) + Num b => (b, b) which, under usual rules of precedence, becomes > foo :: Num a => ((a, a) + Num b) => (b, b) Note how the + binds tighter than the =>. Richard -------------- next part -------------- An HTML attachment was scrubbed... URL: From zocca.marco at gmail.com Sat Sep 5 20:49:55 2020 From: zocca.marco at gmail.com (Marco Zocca) Date: Sat, 5 Sep 2020 22:49:55 +0200 Subject: [Haskell-cafe] call tree of cost centers in a .prof file Message-ID: Hi all, I don't understand the tree-shaped visualizations produced by profiteur, profiterole and ghc-prof-flamegraph . Specifically, I initially thought that cost centers that are displayed closer to the leaves would be due to calls higher up in the tree, but that doesn't seem to be the case. Example; I've produced this basic visualization with ghc-prof and Data.Tree.drawTree (which mirrors qualitatively what flamegraph et al. produce) : MAIN 100.0 | +- Main.trained 100.0 | | | +- ER.ML.Classification.logreg 99.2 | | | | | +- ER.ML.Classification.irls 96.4 | | | | | | | +- ER.ML.Classification.hessianC 30.3 | | | | | | | | | +- ER.Math.Matrix.outerD 30.3 | | | | | | | | | | | `- Main.binMixtureData 29.8 | | | | | | | | | | | +- ER.Math.Random.SplitMix.mvNormalSM 28.8 | | | | | | | | | | | | | `- ER.Math.Matrix.matVec 28.2 | | | | | | | | | | | `- ER.ML.Common.centerDataWMEC 1.0 It looks fine down to level 4 (trained calls logreg, which calls irls, which calls hessianC, which calls outerD), and then it stops making sense (binMixtureData is not ever called by outerD). Could someone please shed some light on this? Thank you in advance From ollie at ocharles.org.uk Sat Sep 5 21:09:31 2020 From: ollie at ocharles.org.uk (Oliver Charles) Date: Sat, 05 Sep 2020 22:09:31 +0100 Subject: [Haskell-cafe] call tree of cost centers in a .prof file In-Reply-To: References: Message-ID: Is a thunk produced by binMixtureData is *forced* by outerD? On Sat, 5 Sep 2020, at 9:49 PM, Marco Zocca wrote: > Hi all, > > I don't understand the tree-shaped visualizations produced by > profiteur, profiterole and ghc-prof-flamegraph . > > Specifically, I initially thought that cost centers that are displayed > closer to the leaves would be due to calls higher up in the tree, but > that doesn't seem to be the case. > > Example; I've produced this basic visualization with ghc-prof and > Data.Tree.drawTree (which mirrors qualitatively what flamegraph et al. > produce) : > > MAIN 100.0 > | > +- Main.trained 100.0 > | | > | +- ER.ML.Classification.logreg 99.2 > | | | > | | +- ER.ML.Classification.irls 96.4 > | | | | > | | | +- ER.ML.Classification.hessianC 30.3 > | | | | | > | | | | +- ER.Math.Matrix.outerD 30.3 > | | | | | | > | | | | | `- Main.binMixtureData 29.8 > | | | | | | > | | | | | +- ER.Math.Random.SplitMix.mvNormalSM 28.8 > | | | | | | | > | | | | | | `- ER.Math.Matrix.matVec 28.2 > | | | | | | > | | | | | `- ER.ML.Common.centerDataWMEC 1.0 > > It looks fine down to level 4 (trained calls logreg, which calls irls, > which calls hessianC, which calls outerD), and then it stops making > sense (binMixtureData is not ever called by outerD). > > Could someone please shed some light on this? > > Thank you in advance > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From kindaro at gmail.com Sun Sep 6 09:47:58 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Sun, 6 Sep 2020 14:47:58 +0500 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. Message-ID: Hello. I have a problem trying to do something very simple. Consider this conversation with GHC: λ data Y α = Y {y ∷ α} λ defaultY = Y {y = mempty} λ :type defaultY defaultY :: Monoid α => Y α λ :type defaultY {y = "c"} ∷ Y String :1:1: error: • Ambiguous type variable ‘α0’ arising from a use of ‘defaultY’ prevents the constraint ‘(Monoid α0)’ from being solved. Probable fix: use a type annotation to specify what ‘α0’ should be. These potential instances exist: instance Monoid a => Monoid (IO a) -- Defined in ‘GHC.Base’ instance Monoid Ordering -- Defined in ‘GHC.Base’ instance Semigroup a => Monoid (Maybe a) -- Defined in ‘GHC.Base’ ...plus 7 others (use -fprint-potential-instances to see them all) • In the expression: defaultY In the expression: defaultY {y = "c"} :: Y String Why does this not work? From lemming at henning-thielemann.de Sun Sep 6 10:33:23 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Sun, 6 Sep 2020 12:33:23 +0200 (CEST) Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: References: Message-ID: On Sun, 6 Sep 2020, Ignat Insarov wrote: > I have a problem trying to do something very simple. > > Consider this conversation with GHC: > > λ data Y α = Y {y ∷ α} > λ defaultY = Y {y = mempty} > λ :type defaultY > defaultY :: Monoid α => Y α > λ :type defaultY {y = "c"} ∷ Y String > > :1:1: error: > • Ambiguous type variable ‘α0’ arising from a use of ‘defaultY’ > prevents the constraint ‘(Monoid α0)’ from being solved. > Probable fix: use a type annotation to specify what ‘α0’ should be. > These potential instances exist: > instance Monoid a => Monoid (IO a) -- Defined in ‘GHC.Base’ > instance Monoid Ordering -- Defined in ‘GHC.Base’ > instance Semigroup a => Monoid (Maybe a) -- Defined in ‘GHC.Base’ > ...plus 7 others > (use -fprint-potential-instances to see them all) > • In the expression: defaultY > In the expression: defaultY {y = "c"} :: Y String > > Why does this not work? I think it should work: :type (defaultY::Y [()]) {y = "c"} The problem is, that GHCi cannot infer the type of defaultY, because the update changes the type. By adding the type annotation Y String at the end, you only tell GHCi what it already knows, namely, that the result of the update has type Y String. But there is no way to infer what the type of 'mempty' must have been before the update. From ietf-dane at dukhovni.org Sun Sep 6 11:35:50 2020 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Sun, 6 Sep 2020 07:35:50 -0400 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: References: Message-ID: <20200906113550.GJ44511@straasha.imrryr.org> On Sun, Sep 06, 2020 at 02:47:58PM +0500, Ignat Insarov wrote: > Consider this conversation with GHC: > > λ data Y α = Y {y ∷ α} > λ defaultY = Y {y = mempty} > λ :type defaultY > defaultY :: Monoid α => Y α > λ :type defaultY {y = "c"} ∷ Y String The mistake is to think that the expression: record { field = value } modifies *that* input record. But in fact, it should be clear that it produces a *new* record (Haskell values are immutable). If the record type is polymorphic: data Record a = Record { field :: a } deriving Show we can write: update :: Record a -> b -> Record b update record b = record { field = b } which produces an output whose type is different from the input. We thus observe: Prelude> data Record a = Record { field :: a } deriving Show Prelude> :{ Prelude| update :: Record a -> b -> Record b Prelude| update record b = record { field = b } Prelude| :} Prelude> let x = Record 1 Prelude> x Record {field = 1} Prelude> let y = update x "foo" Prelude> y Record {field = "foo"} This is why the type annotation on the output (easily inferred) is not sufficient, and it is the input "defaultY" that needs an explicit type. You can of course write a type-preserving setter: monoUpdate :: Record a -> a -> Record a monoUpdate record a = record { field = a} with monoUpdate, your example works: Prelude> let defaultR = Record mempty Prelude> :{ Prelude| monoUpdate :: Record a -> a -> Record a Prelude| monoUpdate record a = record { field = a} Prelude| :} Prelude> monoUpdate defaultR "c" Record {field = "c"} the type morphing behaviour of field update setters can admittedly be surprising at first encounter. -- Viktor. From kindaro at gmail.com Sun Sep 6 14:23:01 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Sun, 6 Sep 2020 19:23:01 +0500 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: <20200906113550.GJ44511@straasha.imrryr.org> References: <20200906113550.GJ44511@straasha.imrryr.org> Message-ID: Thank you Henning and Viktor. Indeed the update of the field goes through if its type is specified beforehand. But I am still not at peace. Consider: λ :type (defaultY ∷ ∀ α. Monoid α ⇒ Y α) {y = "c"} …Error… λ :type (defaultY ∷ Y ( )) {y = "c"} (defaultY ∷ Y ( )) {y = "c"} :: Y [Char] So, polymorphic types are not good enough to update. But see further: λ data Z α = Z {z₁, z₂ ∷ α} λ defaultZ = Z {z₁ = mempty, z₂ = mempty} λ :type (defaultZ ∷ ∀ α. Monoid α ⇒ Z α) {z₁ = "c"} (defaultZ ∷ ∀ α. Monoid α ⇒ Z α) {z₁ = "c"} :: Z [Char] — So when I have two fields it is suddenly fine to update a polymorphic field! I can infer that there is an invisible stage in the type checking process where all expressions must receive a final monomorphic type. I also understand that the surviving field `z₂` serves as a witness of the type `defaultZ` must have been specialized to: `"c" ∷ String` ⇒ `z₂ ∷ String` ⇒ `z₁ ∷ String`, and so type checking can succeed. But still, is this behaviour not obviously wrong? If a value of the transient instantiation of `defaultY` _(that exists only in the moment before its field is updated)_ is never observed, why does it need to be monomorphized? Why not be lazy about it? From evincarofautumn at gmail.com Sun Sep 6 18:13:57 2020 From: evincarofautumn at gmail.com (Jon Purdy) Date: Sun, 6 Sep 2020 11:13:57 -0700 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: References: <20200906113550.GJ44511@straasha.imrryr.org> Message-ID: On Sun, Sep 6, 2020, 7:24 AM Ignat Insarov wrote: > If a value of the > transient instantiation of `defaultY` _(that exists only in the moment > before its field is updated)_ is never observed, why does it need to > be monomorphized? Why not be lazy about it? You could just as well ask why ‘(== [])’ or ‘(== Nothing)’ require an ‘Eq’ constraint even though it will provably never be used. The reason, of course, is that the typechecker is only using purely local reasoning—yes, if you inlined that function far enough into any use site, it would lead to showing the ‘Eq’ redundant; and here, if you examined the update as a whole, you would find the type of the subterm is redundant as well. The same is true for typeclasses generally—instead of having bounded polymorphism, we could just substitute types and see what happens, like C++ templates do, but having the requirements stated in the type improves the predictability of whether something will typecheck, and improves the quality of error messages when it doesn’t. Basically, we accept that types will always be an *approximation* of terms. You can draw the line at different points—with refinement types, you can reason about the particular values or ranges of integers, for instance, instead of the coarse-grained ‘Int’. But all solutions that give you finer granularity also have tradeoffs in terms of ergonomics and predictability. Haskell has largely chosen to draw the line at “syntax-directed” as our criterion for how checking ought to work, assigning a type to every subterm in the same way, without context. This has turned out to be pretty usable in practice, even if there are some frustrating circumstances like this where you as a human can plainly *see* a fact that the typechecker cannot. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lemming at henning-thielemann.de Sun Sep 6 18:19:28 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Sun, 6 Sep 2020 20:19:28 +0200 (CEST) Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: References: <20200906113550.GJ44511@straasha.imrryr.org> Message-ID: On Sun, 6 Sep 2020, Ignat Insarov wrote: > So, polymorphic types are not good enough to update. But see further: > > λ data Z α = Z {z₁, z₂ ∷ α} > λ defaultZ = Z {z₁ = mempty, z₂ = mempty} > λ :type (defaultZ ∷ ∀ α. Monoid α ⇒ Z α) {z₁ = "c"} > (defaultZ ∷ ∀ α. Monoid α ⇒ Z α) {z₁ = "c"} :: Z [Char] > > — So when I have two fields it is suddenly fine to update a polymorphic field! Z has only one type parameter. If you change the type of z1, you also change the type of z2. In your example with z1="c" you set the type of z1 to String, but you also claim that the result is consistently typed, thus z2 must also have type String. Since z2 was not touched, it must have the same type in defaultZ. Thus GHCi can infer the type parameter of defaultZ. From tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk Mon Sep 7 12:04:55 2020 From: tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk (Tom Ellis) Date: Mon, 7 Sep 2020 13:04:55 +0100 Subject: [Haskell-cafe] Counterintuitive ScopedTypeVariables behaviour In-Reply-To: <1EECAE7D-3678-4CD6-83D0-5FC8F7CE9A55@gmail.com> References: <20200816121711.GE27294@cloudinit-builder> <1EECAE7D-3678-4CD6-83D0-5FC8F7CE9A55@gmail.com> Message-ID: <20200907120455.GC13933@cloudinit-builder> Thanks, Alexis, for the detailed explanation about how "type variables" in Haskell are doing a sort of "triple duty". I think my unconscious understanding of type variables with distinct names was 1. they can be chosen independently 2. they don't unify 1 is not true, not least because of MultiParamTypeClasses. I can't choose `a` and `b` completely indepently in the following type signature foo :: C a b => a -> b I can only choose them independently to the extent that a `C` instance exists. Moreover, type equality constraints mean that 2 is not true! The `a` and `b` in the following do indeed unify[1] bar :: a ~ b => a -> b The simple world that Hindly-Milner-Damas created for us becomes more complex when extensions that affect the type system come into play. I'm reassured that type variable bindings in patterns in case alternatives can now bind monomorphic types, that is case () of (() :: t) -> () is now permitted. The previous behaviour seemed inconsistent. On the other hand I'm still somewhat concerned that the meaning of the syntax can vary depending on whether the type variable is in scope or not. That is, I'm not comfortable that the same syntax is used in baz1 to assert that an expression *has a type* as is used in baz2 to *create a fresh name* for a type that an expression has. baz1 :: forall a. a -> () baz1 a = case () of (() :: a) -> () baz2 :: forall a. a -> () baz2 a = case () of (() :: b) -> () "let" does not suffer this inconsistency. The latter is simply not supported. I realise that some syntax must offer the ability to come up with a fresh name, I'm just not convinced that this overloading is the way to do it. To me it feels like "let x = exp" meaning "if `x` is not in scope then bind the value of `exp` to the variable `x`. If `x` is in scope, then check that it is equal to `exp` and if not then crash.". Anyway, I'm sure that opens a whole new can of worms, so thanks for your clarifying remarks to help get my understanding to here! Tom [1] Perhaps I'm conflating an implementation ("unification") with a specification (some semantic notion of what "type equality" means) but I hope my intention here is clear enough. On Mon, Aug 17, 2020 at 03:52:22PM -0500, Alexis King wrote: > I think this behavior is obscured by the way Haskell uses the term “type variable” to refer to three different things: > > Variables that refer to types and are bound within the type namespace. > > Skolem type constants introduced by a `forall`. > > Solver/unification variables, aka “metavariables”, which aren’t really types at all but holes to be filled in with types. > > These are all subtly different, but they are so frequently treated as if they were the same that I suspect listing them out is not actually enough for many people to understand the distinctions! So let me elaborate. > > Suppose we view `forall` like a type-level lambda, so `forall a. a -> a` is analogous to this term: > > \a -> a :-> a > > Clearly, there is a difference between the variable `a` in the above expression and the value we eventually substitute for it when we apply the lambda. When we instantiate the function by doing, say, `id @Int`, we substitute `Int` for `a` to get `Int -> Int`. So `a` itself isn’t a type, it’s a name that refers to a type—a type variable. > > Under this interpretation, there’s nothing wrong with your example. In > > add (x :: a) (y :: b) = x + y > > the variable `a` names `x`’s type, and the variable `b` names `y`’s type, and there’s nothing at all wrong with having two different names that refer to the same thing. It would be like writing this term: > > \t -> let { a = t; b = t } in a :-> b > > So why does it seem confusing? Well, because if we write > > add :: Num t => a -> b -> t > > we get a type error, not two aliases for t. This is because GHC will implicitly quantify all the variables in the type signature to get > > add :: forall t a b. Num t => a -> b -> t > > so `a` and `b` are now distinct, “lambda-bound” variables, not aliases for `t`. > > This implicit quantification means we don’t often think about the type variables we write in type signatures as being like term variables, which are just names that reference other things. Rather, we think of type variables as being like type constants, and in fact GHC encourages this interpretation in its error reporting. > > To explain, consider what happens when GHC checks `add` against the type we wrote above. Somehow, it needs to figure out the types of `x` and `y`, but it can’t just say > > x :: a > y :: b > > because those variables were bound by the `forall` (they are “lambda-bound”), and if we move them into some other context where they appear free, they no longer have any meaning. GHC has to somehow pick actual types for x and y, not type variables. A naïve idea would be to invent two new solver variables, aka “holes to be filled in with inferred types”, yielding something like > > a1, b1 fresh > x :: a1 > y :: b1 > > but this doesn’t work. Imagine we did that while typechecking this function: > > bad :: a -> a > bad _ = True > > If we invented a new solver variable to use in place of `a` while typechecking `bad`, GHC would just say “oh, I can fill in the hole with Bool” and carry on, which would be quite bad! The whole point of parametricity is that the function must work with any type. > > So GHC actually does something else. Behind the scenes, it creates two new type definitions, distinct from all other types in the program: > > data FakeTypeA > data FakeTypeB > > Now it typechecks the function using these fake types substituted for `a` and `b`: > > x :: FakeTypeA > y :: FakeTypeB > > Now when GHC goes to typecheck `x + y`, it will raise a type error, since FakeTypeA and FakeTypeB are obviously not the same type. This enforces parametricity. > > Of course, it would be very confusing to the user to see names like “FakeTypeA” in their type errors. So GHC pulls some sleight of hand, and it prints the new types it generates as just “a” and “b”, the same as the forall-bound variables it created them for. These don’t look like type constants, because in source Haskell, all type constants start with UpperCase. But behind the scenes, they really are type constants, not type variables. GHC calls these fake type constants “rigid variables” or “skolem variables,” but don’t be fooled: they’re not variables at all, they’re constants. > > GHC tells this white lie to keep programmers from having to think about all this behind the scenes business. When GHC says “could not match ‘a’ with ‘b’,” it’s natural for programmers to think “ah, these are the ‘a’ and ‘b’ I wrote in my type signature,” and they learn to treat “type variables” and “rigid type constants” as one and the same. They don’t think about how this is a meaningfully different definition of “variable” than what they’re used to at the term level. This works out okay because the only way to bind type variables in source Haskell 98/2010 is universal quantification. > > But now ScopedTypeVariables and ExistentialQuantification enter the picture, and there’s a problem: we suddenly need to be able to bind type variables in a place that isn’t `forall`. Now we have type variable binding sites that look just like type variable uses, but they aren’t implicitly-quantified the way they are in standalone type signatures, and confusion ensues. > > This confusion actually goes much deeper than you might expect. Because Haskell historically hasn’t syntactically distinguished type variable uses from binding sites, we have new ambiguous situations like this: > > \(x :: Maybe a) -> ... > > Is this a type annotation on x, which states that x should have type `Maybe a`, where `a` is a type variable already in scope? Or is it a pattern binding that matches against x’s type, which asserts that it has the shape `Maybe t` and binds a new variable `a` to the type `t`? And it gets even worse! Suppose we have this: > > f (Foo @a x) = ... > > Is `@a` a visible type application of `Foo` to the in-scope type `a`, instantiating the (universally-quantified) `Foo` constructor at a particular type? Or is it a binder for an existential variable “stored inside” `Foo`? Both are reasonable (and useful) interpretations. > > A GHC proposal that presents a design that sorts all these ambiguities out would be most welcome. To my knowledge, no such proposal currently exists, but I could be wrong—I know this issue has been discussed on some proposals (such as visible type applications in patterns), so maybe someone has come up with something I’m not aware of. > > Alexis > > > On Aug 16, 2020, at 07:17, Tom Ellis wrote: > > > > I have just noticed that with ScopedTypeVariables one can write the > > following bizarre definition > > > > -- Inferred signature: > > -- add :: Num a => a -> a -> a > > add (x :: a) (y :: b) = x + y > > > > The reason for this behaviour is that > > > >> in all patterns other than pattern bindings, a pattern type > >> signature may mention a type variable that is not in scope; in this > >> case, the signature brings that type variable into scope. > > > > https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#pattern-type-signatures > > > > and the justification is that > > > >> Bringing type variables into scope is particularly important for > >> existential data constructors > > > > But this leads to a rather puzzling user experience. Was it really > > not possible to design this extension in a way that would allow > > bringing new type variables into scope locally but not allow them to > > unify with other type variables from outer scopes? > > > > To be more concrete, what I would like to see is a design where `k` is > > allowed because the type `a` (bound within a pattern) does not need to > > unify with anything but `k2` is not allowed because `a` is forbidden > > to unify with `b`. `k3` would also be forbidden (the type variables > > called `a` are actually distinct) as would `k4` (the type variable for > > the inferred signature would be fresh and distinct from `a`). > > > > I believe this design might lead to much less puzzling behaviour. > > Would it be possible (within a new extension, of course) or have I > > overlooked something? > > > > Tom > > > > > > data T = forall a. MkT [a] > > > > k :: T -> T > > k (MkT [t::a]) = > > MkT t3 > > where > > (t3::[a]) = [t,t,t] > > > > data T2 a = MkT2 [a] > > > > k2 :: T2 b -> T2 b > > k2 (MkT2 [t::a]) = > > MkT2 t3 > > where > > (t3::[a]) = [t,t,t] > > > > k3 :: T2 a -> T2 a > > k3 (MkT2 [t::a]) = > > MkT2 t3 > > where > > (t3::[a]) = [t,t,t] > > > > k4 (MkT2 [t::a]) = > > MkT2 t3 > > where > > (t3::[a]) = [t,t,t] > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. From olf at aatal-apotheke.de Mon Sep 7 19:25:33 2020 From: olf at aatal-apotheke.de (Olaf Klinke) Date: Mon, 07 Sep 2020 21:25:33 +0200 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. Message-ID: I take it that in the expression (defaultY ∷ ∀ α. Monoid α ⇒ Y α) {y = "c"} the compiler is not smart enough to infer that the concrete value of α is irrelevant for the outcome and therefore rejects the program. The obligation here is to prove that the function λ x. x {y = "c"} :: ∀ α. Y α -> Y String is constant across all possible values of α, which I guess is undecidable for general one-parameter types? Or is there a free theorem saying that whenever a function has this general type signature, then the α cannot possibly be used? This certainly fails if Y has more type parameters that remain constant in the expression: A type class could distinguish the extra type parameters even if at the value level the result is constant across all α. Olaf From rae at richarde.dev Tue Sep 8 16:12:21 2020 From: rae at richarde.dev (Richard Eisenberg) Date: Tue, 8 Sep 2020 16:12:21 +0000 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: References: Message-ID: <010f01746e7d5422-e1c40a99-8b25-4f50-8294-d2e0171e5087-000000@us-east-2.amazonses.com> Hi Olaf, I could understand this question better with a concrete definition of what Y is. Sometimes, the value of (defaultY { y = "c"}) really does depend on `a`. But maybe I'm misunderstanding your intent. Richard > On Sep 7, 2020, at 3:25 PM, Olaf Klinke wrote: > > I take it that in the expression > > (defaultY ∷ ∀ α. Monoid α ⇒ Y α) {y = "c"} > > the compiler is not smart enough to infer that the concrete value of α > is irrelevant for the outcome and therefore rejects the program. The > obligation here is to prove that the function > > λ x. x {y = "c"} :: ∀ α. Y α -> Y String > > is constant across all possible values of α, which I guess is > undecidable for general one-parameter types? Or is there a free theorem > saying that whenever a function has this general type signature, then > the α cannot possibly be used? This certainly fails if Y has more type > parameters that remain constant in the expression: A type class could > distinguish the extra type parameters even if at the value level the > result is constant across all α. > > Olaf > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. From rae at richarde.dev Tue Sep 8 18:16:28 2020 From: rae at richarde.dev (Richard Eisenberg) Date: Tue, 8 Sep 2020 18:16:28 +0000 Subject: [Haskell-cafe] Counterintuitive ScopedTypeVariables behaviour In-Reply-To: <20200907120455.GC13933@cloudinit-builder> References: <20200816121711.GE27294@cloudinit-builder> <1EECAE7D-3678-4CD6-83D0-5FC8F7CE9A55@gmail.com> <20200907120455.GC13933@cloudinit-builder> Message-ID: <010f01746eeef521-889033d0-5df1-44e6-859f-340319aa05f5-000000@us-east-2.amazonses.com> > On Sep 7, 2020, at 8:04 AM, Tom Ellis wrote: > > On the > other hand I'm still somewhat concerned that the meaning of the syntax > can vary depending on whether the type variable is in scope or not. Yes. This has been a spot of discomfort for some time (at least for me, and I know I'm not completely alone). A recent accepted, unimplemented proposal (https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0285-no-implicit-binds.rst) describes -XNoPatternSignatureBinds, which disallows not-in-scope variables from appearing in pattern signatures. Instead, the variable must come into scope some other way. That "other way" depends on proposals not yet completely formed/accepted, but once the dust has settled, I think you'll get what you want with -XNoPatternSignatureBinds. Richard -------------- next part -------------- An HTML attachment was scrubbed... URL: From olf at aatal-apotheke.de Wed Sep 9 07:16:52 2020 From: olf at aatal-apotheke.de (Olaf Klinke) Date: Wed, 09 Sep 2020 09:16:52 +0200 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: <010f01746e7d5422-e1c40a99-8b25-4f50-8294-d2e0171e5087-000000@us-east-2.amazonses.com> References: <010f01746e7d5422-e1c40a99-8b25-4f50-8294-d2e0171e5087-000000@us-east-2.amazonses.com> Message-ID: <1b6d1dd5bd9536d12ca75b4ead3fa24f089541fc.camel@aatal-apotheke.de> On Tue, 2020-09-08 at 16:12 +0000, Richard Eisenberg wrote: > Hi Olaf, > > I could understand this question better with a concrete definition of > what Y is. Sometimes, the value of (defaultY { y = "c"}) really does > depend on `a`. But maybe I'm misunderstanding your intent. The definition of Y is the one given by Ignat in his OP of this thread, which essentially is Y ~ Data.Functor.Identity. We know very well that λ x. x {runIdentity = "c"} :: ∀ α. Identity α -> Identity String is indeed independent of α and in this special case the compiler seems to infer this [*]. But first-year CS students learn that any non- trivial property of a program is undecidable, whence I'm hesitating to blame GHC for not giving Ignat the behaviour he expects. Probable explanation: It *is* a free theorem that for any concrete type T, any function f :: ∀ α. α -> T can not possibly use its argument and hence must be constant, if type families are not involved. So my question is whether this free theorem still holds when α is wrapped in any one-parameter type constructor. Ignat's OP involved a constraint, too, and I see that this complicates matters. Consider foo :: ∀ α. (Show α, Default α) => Identity α foo = Identity def notConstant = (runIdentity . fmap show) foo :: String which at first glance looks like one could apply the free theorem but the value clearly depends on the choice of α. GHCi in fact accepts this expression and substitutes α = () when asked to print notConstant, but GHC rightfully rejects the program. Olaf [*] The following compiles: import Data.Functor.Identity main = print . runIdentity $ (Identity undefined) {runIdentity = "c"} > > > > I take it that in the expression > > > > (defaultY ∷ ∀ α. Monoid α ⇒ Y α) {y = "c"} > > > > the compiler is not smart enough to infer that the concrete value > > of α > > is irrelevant for the outcome and therefore rejects the program. > > The > > obligation here is to prove that the function > > > > λ x. x {y = "c"} :: ∀ α. Y α -> Y String > > > > is constant across all possible values of α, which I guess is > > undecidable for general one-parameter types? Or is there a free > > theorem > > saying that whenever a function has this general type signature, > > then > > the α cannot possibly be used? This certainly fails if Y has more > > type > > parameters that remain constant in the expression: A type class > > could > > distinguish the extra type parameters even if at the value level > > the > > result is constant across all α. > > > > Olaf > > > > _______________________________________________ > > Haskell-Cafe mailing list > > To (un)subscribe, modify options or view archives go to: > > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > > Only members subscribed via the mailman list are allowed to post. From anthony_clayden at clear.net.nz Thu Sep 10 06:18:42 2020 From: anthony_clayden at clear.net.nz (Anthony Clayden) Date: Thu, 10 Sep 2020 18:18:42 +1200 Subject: [Haskell-cafe] Constraints as a moveable feast? Message-ID: > On *Tue Sep 1 21:18:40 UTC 2020, ** Richard Eisenberg wrote:* *> *I see the core idea as a fairly straightforward generalization of the "Partial Type Constructors" work Ah. Now I see a bit more of what you're getting at. We want as if the type synonym were an associated type family with a most-general instance class Num a => CPoint a where type Point a instance Num a => CPoint a where type Point a = (a, a) The semantics to be as the PTC paper, such that expanding `Point a` always requires a `CPoint a` instance, which will in turn want a `Num a`. And the expansion to be immediate (as with type synonyms) so that the client can put `Point a` in instance heads, for example. > But I really don't know what it means to expand without adding parens. ... what about > *pointX :: Point a -> a* *> ... *We don't want that type to expand to (Num a => (a, a)) -> a Ok, it was your using fancy type operators that put me off. We can represent the AST (and show the need for parens) by reverting to prefix syntax, within H98: pointX :: (->) (Point a) a -- needs parens around (Point a) And yes my naieve syntactic expansion would give the type you show, wot we do not want. AntC -------------- next part -------------- An HTML attachment was scrubbed... URL: From kindaro at gmail.com Thu Sep 10 06:45:45 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Thu, 10 Sep 2020 11:45:45 +0500 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: <1b6d1dd5bd9536d12ca75b4ead3fa24f089541fc.camel@aatal-apotheke.de> References: <010f01746e7d5422-e1c40a99-8b25-4f50-8294-d2e0171e5087-000000@us-east-2.amazonses.com> <1b6d1dd5bd9536d12ca75b4ead3fa24f089541fc.camel@aatal-apotheke.de> Message-ID: Thank you all kind folks who responded. I think a leap of reasoning has occurred and I would like to ask you all to slow down and notice it. Record update is special syntax. It is not a function. It is not as general as a function. It is not perceived nor written as a function. Ain't no lambda in it. You may say that it desugars to a function. But as a user of the language, I am unconcerned: desugar another way then! Surely a function of type `C α ⇒ Y α → Y String` may assign different strings to `y`, by consulting methods of `C`. But a record update cannot! Neither the value `y₀` nor the type variable α are ever in the scope of the update expression. From tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk Thu Sep 10 09:03:50 2020 From: tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk (Tom Ellis) Date: Thu, 10 Sep 2020 10:03:50 +0100 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: References: <010f01746e7d5422-e1c40a99-8b25-4f50-8294-d2e0171e5087-000000@us-east-2.amazonses.com> <1b6d1dd5bd9536d12ca75b4ead3fa24f089541fc.camel@aatal-apotheke.de> Message-ID: <20200910090350.GN13933@cloudinit-builder> On Thu, Sep 10, 2020 at 11:45:45AM +0500, Ignat Insarov wrote: > Thank you all kind folks who responded. > > I think a leap of reasoning has occurred and I would like to ask you > all to slow down and notice it. > > Record update is special syntax. > > It is not a function. It is not as general as a function. It is not > perceived nor written as a function. Ain't no lambda in it. > > You may say that it desugars to a function. But as a user of the > language, I am unconcerned: desugar another way then! > > Surely a function of type `C α ⇒ Y α → Y String` may assign different > strings to `y`, by consulting methods of `C`. But a record update > cannot! Neither the value `y₀` nor the type variable α are ever in the > scope of the update expression. Why do you say it has anything to do with record update being special? The same problem occurs with a true function: Prelude> data Y a = Y { y :: a } Prelude> let defaultY = Y { y = mempty } Prelude> let setYc y = y { y = "c" } Prelude> :t defaultY defaultY :: Monoid a => Y a Prelude> :t setYc setYc :: Y a -> Y [Char] Prelude> :t setYc defaultY :1:7: error: • Ambiguous type variable ‘a0’ arising from a use of ‘defaultY’ prevents the constraint ‘(Monoid a0)’ from being solved. Probable fix: use a type annotation to specify what ‘a0’ should be. These potential instances exist: instance Monoid a => Monoid (IO a) -- Defined in ‘GHC.Base’ instance Monoid Ordering -- Defined in ‘GHC.Base’ instance Semigroup a => Monoid (Maybe a) -- Defined in ‘GHC.Base’ ...plus 7 others (use -fprint-potential-instances to see them all) • In the first argument of ‘setYc’, namely ‘defaultY’ In the expression: setYc defaultY From kindaro at gmail.com Thu Sep 10 09:47:23 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Thu, 10 Sep 2020 14:47:23 +0500 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: <20200910090350.GN13933@cloudinit-builder> References: <010f01746e7d5422-e1c40a99-8b25-4f50-8294-d2e0171e5087-000000@us-east-2.amazonses.com> <1b6d1dd5bd9536d12ca75b4ead3fa24f089541fc.camel@aatal-apotheke.de> <20200910090350.GN13933@cloudinit-builder> Message-ID: What I meant to express is that this problem _does not need to occur_ with record updates, even though they are presently being desugared to functions. In other words, I propose that record updates _are_ special — or _should be_ special, in any case. From tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk Thu Sep 10 10:03:59 2020 From: tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk (Tom Ellis) Date: Thu, 10 Sep 2020 11:03:59 +0100 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: References: <010f01746e7d5422-e1c40a99-8b25-4f50-8294-d2e0171e5087-000000@us-east-2.amazonses.com> <1b6d1dd5bd9536d12ca75b4ead3fa24f089541fc.camel@aatal-apotheke.de> <20200910090350.GN13933@cloudinit-builder> Message-ID: <20200910100359.GO13933@cloudinit-builder> On Thu, Sep 10, 2020 at 02:47:23PM +0500, Ignat Insarov wrote: > What I meant to express is that this problem _does not need to occur_ > with record updates, even though they are presently being desugared to > functions. In other words, I propose that record updates _are_ special > — or _should be_ special, in any case. Ah, I see. You are saying that record updates can have a special typing rule because we have extra information that tells us that the type of the result cannot depend on the type of what was previously there. From ietf-dane at dukhovni.org Thu Sep 10 11:00:48 2020 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Thu, 10 Sep 2020 07:00:48 -0400 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: <20200910100359.GO13933@cloudinit-builder> References: <010f01746e7d5422-e1c40a99-8b25-4f50-8294-d2e0171e5087-000000@us-east-2.amazonses.com> <1b6d1dd5bd9536d12ca75b4ead3fa24f089541fc.camel@aatal-apotheke.de> <20200910090350.GN13933@cloudinit-builder> <20200910100359.GO13933@cloudinit-builder> Message-ID: <20200910110048.GH4758@straasha.imrryr.org> On Thu, Sep 10, 2020 at 11:03:59AM +0100, Tom Ellis wrote: > On Thu, Sep 10, 2020 at 02:47:23PM +0500, Ignat Insarov wrote: > > What I meant to express is that this problem _does not need to occur_ > > with record updates, even though they are presently being desugared to > > functions. In other words, I propose that record updates _are_ special > > — or _should be_ special, in any case. > > Ah, I see. You are saying that record updates can have a special > typing rule because we have extra information that tells us that the > type of the result cannot depend on the type of what was previously > there. That could be a surprising situation, there'd be things one could do with the record update setters, that one could not do with Lens setters. The work-around is to no define a type-preserving setter: monoUpdate :: Y a -> a -> Y a monoUpdate r a = r { y = a } and use it to construct values of Y from a polymorphic seed. One might even note that: defaultY :: Monoid a => Y a defaultY = Y mempty is a itself not a record, because it does not have a concrete type. It is a polymorphic nullary function that can produce a record, given a context in which the desired type is apparent (can be inferred). Therefore, defaultY { y = "c" } is not an update of an existing input record, but rather it is an (attempted) update of a new record, to be created there and then via a call to the defaultY "factory". But that call does not provide a context to determine the type (and thus value) of defaultY. Yes, we reason that it couldn't matter, but the compiler has limited resources, and there's only so much inference that is compatible with not slowing down compile times beyond their their current heft. The kind of inference needed to conclude that the type does not matter is not built-in. So, to me, needing to assign an explicit type in this corner case is just an ordinary cost of doing functional programming (rooted in the pure lambda calculus, where its functions all the way down). I found it's initially somewhat unintuitive behaviour actually rather educational. Taking the time to understand it clarified some key ideas. -- Viktor. From ietf-dane at dukhovni.org Thu Sep 10 11:28:09 2020 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Thu, 10 Sep 2020 07:28:09 -0400 Subject: [Haskell-cafe] Cannot update a field in a record with a polymorphic type. In-Reply-To: <20200910100359.GO13933@cloudinit-builder> References: <010f01746e7d5422-e1c40a99-8b25-4f50-8294-d2e0171e5087-000000@us-east-2.amazonses.com> <1b6d1dd5bd9536d12ca75b4ead3fa24f089541fc.camel@aatal-apotheke.de> <20200910090350.GN13933@cloudinit-builder> <20200910100359.GO13933@cloudinit-builder> Message-ID: <20200910112809.GI4758@straasha.imrryr.org> On Thu, Sep 10, 2020 at 11:03:59AM +0100, Tom Ellis wrote: > Ah, I see. You are saying that record updates can have a special > typing rule because we have extra information that tells us that the > type of the result cannot depend on the type of what was previously > there. Here's an example where the previous value matters: λ> data Y a = Y { y :: !a } deriving Show λ> defaultY :: Monoid a => Y a ; defaultY = Y undefined λ> monoUpdate :: Y a -> a -> Y a; monoUpdate r a = r { y = a } λ> monoUpdate defaultY "c" *** Exception: Prelude.undefined CallStack (from HasCallStack): error, called at libraries/base/GHC/Err.hs:79:14 in base:GHC.Err undefined, called at :2:44 in interactive:Ghci2 [ The construction of "defaultY { y = "c" }" begins with a construction of "defaultY" that may fail. ] Now imagine that some Monoid instances have "mempty = undefined", and others do not. Making the constructor strict, When the struct record is strict in the updated field, (e.g. with StrictData), the initial value is constructed strictly, even with optimisation. For example the below compiled with "ghc -O2" still throws an exception when executed: {-# LANGUAGE StrictData #-} module Main (main) where data Y a = Y { y :: a } deriving Show defaultY :: Monoid a => Y a; defaultY = Y undefined monoUpdate :: Y a -> a -> Y a; monoUpdate r a = r { y = a } main :: IO () main = print $ monoUpdate defaultY "c" Turning off "StrictData" makes it go. So I think there's even less room here for special logic. The type ambiguity must be resolved, allowing defaultY to be constructed. -- Viktor. From compl.yue at icloud.com Thu Sep 10 13:44:43 2020 From: compl.yue at icloud.com (YueCompl) Date: Thu, 10 Sep 2020 21:44:43 +0800 Subject: [Haskell-cafe] Need help to get started with GHC.Generics Message-ID: Dear Cafe, I'm tinkering with the idea for arbitrary Haskell functions to be easily called from scripting code, I see auto derive with GHC.Generics might be the most promising tool, but I'm lost after read https://wiki.haskell.org/GHC.Generics and hackage docs. I have no clue so far with how to start with it. Specifically I want the section highlighted in blue get auto generated, within the following `runghc` ready example: ``` {-# LANGUAGE BangPatterns #-} module Main where import Prelude import GHC.Generics import Data.Dynamic -- * minimum data structures as interface with scripting code type AttrKey = String data AttrVal = NilValue | IntValue !Integer | StrValue !String deriving (Eq, Ord, Typeable) instance Show AttrVal where show NilValue = "nil" show (IntValue !x) = show x show (StrValue !x) = show x data ArgsPack = ArgsPack { positional'args :: [AttrVal] , keyword'args :: [(AttrKey, AttrVal)] } instance Semigroup ArgsPack where (ArgsPack p1 kw1) <> (ArgsPack p2 kw2) = ArgsPack (p1 ++ p2) (kw1 ++ kw2) instance Monoid ArgsPack where mempty = ArgsPack [] [] class Callable a where call :: a -> ArgsPack -> (AttrVal -> IO ()) -> IO () -- * functions to be callable from scripting code newtype Assert = Assert ( Expect -> Maybe Target -> Message -> IO Message ) type Expect = AttrVal type Target = AttrVal type Message = String instance Callable Assert where -- can this get auto-generated ? with https://wiki.haskell.org/GHC.Generics call (Assert !assert) (ArgsPack !args !kwargs) !exit = do (expect, target, message) <- parseApk result <- assert expect target message exit $ StrValue result where parseApk :: IO (Expect, Maybe Target, Message) parseApk = goParse (Left "missing arg: expect", Nothing, Left "missing arg: message") args kwargs goParse (got'expect, got'target, got'message) [] [] = case got'expect of Left msg -> error msg Right expect -> case got'message of Left msg -> error msg Right message -> return (expect, got'target, message) goParse (got'expect, got'target, got'message) args' ((name, val) : kwargs') = case name of "expect" -> case got'expect of Right{} -> error "duplicate arg: expect" Left{} -> goParse (Right val, got'target, got'message) args' kwargs' "target" -> case got'target of Just{} -> error "duplicate arg: target" Nothing -> goParse (got'expect, Just val, got'message) args' kwargs' "message" -> case got'message of Right{} -> error "duplicate arg: message" Left{} -> case val of StrValue message -> goParse (got'expect, got'target, Right message) args' kwargs' _ -> error "bad arg type for: message" _ -> error "unexpected keyword args" goParse (got'expect, got'target, got'message) (val : args') [] = case got'expect of Left{} -> goParse (Right val, got'target, got'message) args' [] Right{} -> case got'target of Nothing -> goParse (got'expect, Just val, got'message) args' [] Just{} -> case got'message of Left{} -> case val of StrValue message -> goParse (got'expect, got'target, Right message) args' [] _ -> error "bad arg type for: message" Right{} -> error "extranous positional args" -- mockup & test out main :: IO () main = call (Assert assert) (ArgsPack [IntValue 333, StrValue "as good will"] [("target", IntValue 333)] ) $ \result -> putStrLn $ "Got result: " <> show result -- | plain Haskell function meant to be easily called by scripting code assert :: Expect -> Maybe Target -> Message -> IO Message assert !expect !maybeTarget !message = case maybeTarget of Nothing -> return $ "* assertion not applicable: " <> message Just target -> if expect == target then return $ "* assertion passed: " <> message else error $ "* assertion failed: " <> message ``` I tried to understand how The compiler can provide a default generic implementation for parseJSON . is implemented in [aeson](https://hackage.haskell.org/package/aeson ) and it is overwhelming to me at the moment ... Is there easier scaffold template for me to start with GHC.Generics? Or there're even better techniques to achieve my final goal? Help please! Best regards, Compl -------------- next part -------------- An HTML attachment was scrubbed... URL: From luc.duponcheel at gmail.com Thu Sep 10 15:01:55 2020 From: luc.duponcheel at gmail.com (Luc Duponcheel) Date: Thu, 10 Sep 2020 17:01:55 +0200 Subject: [Haskell-cafe] Implementing parallelism using actors. How to improve the code. Message-ID: -- Hello, I am Luc Duponcheel. -- I started as a Haskell programmer, -- went to Java for a living, -- naturally evolved to Scala, -- and, being retired now, -- went back to my first love ... -- I am working on a library -- _______ __ __ _______ -- / ___ /\ / /\ / /\ / ___ /\ -- / /__/ / / _____/ / / / /_/__ / /__/ / / -- / _____/ / / ___ / / / ___ /\ /____ / / -- / /\____\/ / /__/ / / / /__/ / / \___/ / / -- /_/ / /______/ / /______/ / /_/ / -- \_\/ \______\/ \______\/ \_\/ -- v1.0 -- Program Description Based Programming Library -- author Luc Duponcheel 2020 - ... -- I started writing code in Scala (presented it at several conferences). -- I encountered issues with the fact that lazyness is not the default evaluation strategy. -- I switched to Haskell. -- In short: the library is about pointfree categorical programming -- (you may wish to have a look at ( https://www.youtube.com/watch?v=23VEcabMk7k (warning low sound volume)) -- below is some code with comments (you can load the code in ghci) -- it works fine but I want to get rid of the extra type class parameters x and w -- I tried to get rid of x and w in two ways -- . using forall -- . using a GADT -- -- both approaches failed -- -- the relevant code changes that I tried are commented out -- they are underneath the code that follows -- the code -- specifies parallelism -- and -- implements it using a dummy version of the hakka library -- (see https://hackage.haskell.org/package/hakka-0.2.0/docs/src/Hakka-Actor.html) -- first I need some language extensions {-# LANGUAGE TypeOperators #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE GADTs #-} -- next I need some imports import Control.Monad.IO.Class import Control.Monad.Trans.Class import Control.Monad.Trans.State.Strict import Control.Monad.Trans.Cont -- next some useful generic code infixr `c` infixr `d` f `c` g = f . g (f `d` g) h = f `c` g h -- -- parallel specification -- -- note the extra type class parameters x and w -- type z && y = (z, y) class Parallel x w to where par :: (z `to` x) -> (y `to` w) -> (z && y) `to` (x && w) -- -- parallel implementation -- -- o msg stands for message -- o mnd stands for monad -- o rst stands for result -- -- -- three actors involved -- -- o reactor -- - reacts by using a continuation cont when both -- . an x result (at left) -- . a w result (at right) -- have been received -- -- o leftActor -- - acts to send an x result (at left) to reactor -- -- o rightActor -- - acts to send a w result (at right) to reactor -- -- reactor sends both leftActor and rightActor a message to let them act -- data ActorRef = ActorRef newtype ActorContext msg = ActorContext msg newtype ActorT msg mnd z = ActorT { runActorT :: StateT (ActorContext msg) mnd z } deriving (Functor, Applicative, Monad, MonadFail, MonadTrans, MonadIO) actor :: String -> (msg -> ActorT msg IO ()) -> ActorT msg IO ActorRef actor name messageHandler = undefined become :: (msg -> ActorT msg IO ()) -> ActorT msg IO () become messageHandler = undefined (!) :: ActorRef -> msg -> ActorT msg IO () actorRef ! message = undefined newtype ReactiveT msg rst mnd z = ReactiveT { runReactiveT :: ContT rst (ActorT msg mnd) z } deriving (Functor, Applicative, Monad, MonadFail, MonadIO) newtype KleisliT mnd z y = KleisliT { runKleisliT :: z -> mnd y } type ReactiveParallelT msg rst mnd = KleisliT (ReactiveT msg rst mnd) data Message x w = LeftReact x | RightReact w | LeftAct | RightAct type ReactiveActorBasedParallelT x w = ReactiveParallelT (Message x w) () IO runReactiveActorBasedParallelT = runContT `d` runReactiveT `d` runKleisliT instance Parallel x w (ReactiveActorBasedParallelT x w) where par z2x y2w = KleisliT (\case (z, y) -> ReactiveT (ContT (\cont -> actor "reactor" (\case LeftReact x -> become (\case RightReact w -> cont (x, w) ) RightReact w -> become (\case LeftReact x -> cont (x, w) ) ) >>= \reactorRef -> actor "leftActor" (\case LeftAct -> runReactiveActorBasedParallelT z2x z (\x -> reactorRef ! LeftReact x) ) >>= \leftActorRef -> actor "rightActor" (\case RightAct -> runReactiveActorBasedParallelT y2w y (\w -> reactorRef ! RightReact w) ) >>= \rightActorRef -> leftActorRef ! LeftAct >> rightActorRef ! RightAct ) ) ) -- class Parallel to where -- data Message = forall x. LeftReact x | forall w. RightReact w | LeftAct | RightAct -- data Message where -- LeftReact :: x -> Message -- RightReact :: w -> Message -- LeftAct :: Message -- RightAct :: Message -- type ReactiveActorBasedParallelT = ReactiveParallelT Message () IO -- instance Parallel (ReactiveActorBasedParallelT IO) where -- __~O -\ <, (*)/ (*) reality goes far beyond imagination -------------- next part -------------- An HTML attachment was scrubbed... URL: From lysxia at gmail.com Thu Sep 10 15:08:48 2020 From: lysxia at gmail.com (Li-yao Xia) Date: Thu, 10 Sep 2020 11:08:48 -0400 Subject: [Haskell-cafe] Need help to get started with GHC.Generics In-Reply-To: References: Message-ID: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> Hi Compl, I couldn't tell what's generic (in the sense of GHC.Generics) about this example. A clearer example would be to give two applications with different algebraic data types, and to show how they consist of the same boilerplate, where the differences are only due to the differing numbers of fields and constructors. As for tutorials on generics, a good starting point might be generics-eot. Its documentation comes with a series of tutorials: https://generics-eot.readthedocs.io/en/stable/ Li-yao On 9/10/2020 9:44 AM, YueCompl via Haskell-Cafe wrote: > Dear Cafe, > > I'm tinkering with the idea for arbitrary Haskell functions to be easily > called from scripting code, I see auto derive with GHC.Generics might be > the most promising tool, but I'm lost after read > https://wiki.haskell.org/GHC.Generics and hackage docs. I have no clue > so far with how to start with it. > > Specifically I want the section highlighted in blue get auto generated, > within the following `runghc` ready example: > > ``` > {-# LANGUAGEBangPatterns#-} > > moduleMain where > > importPrelude > importGHC.Generics > importData.Dynamic > > > -- * minimum data structures as interface with scripting code > > typeAttrKey=String > dataAttrVal=NilValue > |IntValue!Integer > |StrValue!String > deriving(Eq,Ord,Typeable) > instanceShowAttrValwhere > show NilValue="nil" > show (IntValue!x)=show x > show (StrValue!x)=show x > > dataArgsPack=ArgsPack{ > positional'args::[AttrVal] > ,keyword'args::[(AttrKey,AttrVal)] > } > instanceSemigroupArgsPackwhere > (ArgsPackp1 kw1)<>(ArgsPackp2 kw2)=ArgsPack(p1 ++p2)(kw1 ++kw2) > instanceMonoidArgsPackwhere > mempty =ArgsPack[][] > > classCallableawhere > call::a->ArgsPack->(AttrVal->IO())->IO() > > > -- * functions to be callable from scripting code > > newtypeAssert=Assert( > Expect->MaybeTarget->Message->IOMessage > ) > typeExpect=AttrVal > typeTarget=AttrVal > typeMessage=String > > instanceCallableAssertwhere > > -- can this get auto-generated ? with https://wiki.haskell.org/GHC.Generics > call (Assert!assert)(ArgsPack!args !kwargs)!exit =do > (expect,target,message)<-parseApk > result <-assert expect target message > exit $StrValueresult > where > > parseApk::IO(Expect,MaybeTarget,Message) > parseApk =goParse > (Left"missing arg: expect",Nothing,Left"missing arg: message") > args > kwargs > > goParse (got'expect,got'target,got'message)[][]=casegot'expect of > Leftmsg ->error msg > Rightexpect ->casegot'message of > Leftmsg ->error msg > Rightmessage ->return (expect,got'target,message) > goParse (got'expect,got'target,got'message)args' ((name,val):kwargs') > =casename of > "expect"->casegot'expect of > Right{}->error "duplicate arg: expect" > Left{}->goParse (Rightval,got'target,got'message)args' kwargs' > "target"->casegot'target of > Just{}->error "duplicate arg: target" > Nothing->goParse (got'expect,Justval,got'message)args' kwargs' > "message"->casegot'message of > Right{}->error "duplicate arg: message" > Left{}->caseval of > StrValuemessage -> > goParse (got'expect,got'target,Rightmessage)args' kwargs' > _ ->error "bad arg type for: message" > _ ->error "unexpected keyword args" > goParse (got'expect,got'target,got'message)(val :args')[]= > casegot'expect of > Left{}->goParse (Rightval,got'target,got'message)args' [] > Right{}->casegot'target of > Nothing->goParse (got'expect,Justval,got'message)args' [] > Just{}->casegot'message of > Left{}->caseval of > StrValuemessage -> > goParse (got'expect,got'target,Rightmessage)args' [] > _ ->error "bad arg type for: message" > Right{}->error "extranous positional args" > > > -- mockup & test out > main::IO() > main = > call > (Assertassert) > (ArgsPack[IntValue333,StrValue"as good will"] > [("target",IntValue333)] > ) > $\result ->putStrLn $"Got result: "<>show result > > -- | plain Haskell function meant to be easily called by scripting code > assert::Expect->MaybeTarget->Message->IOMessage > assert !expect !maybeTarget !message =casemaybeTarget of > Nothing->return $"* assertion not applicable: "<>message > Justtarget ->ifexpect ==target > thenreturn $"* assertion passed: "<>message > elseerror $"* assertion failed: "<>message > > ``` > > I tried to understand how > > * The compiler can provide a default generic implementation for > |parseJSON > |. > > is implemented in [aeson](https://hackage.haskell.org/package/aeson) and > it is overwhelming to me at the moment ... > > Is there easier scaffold template for me to start with GHC.Generics? Or > there're even better techniques to achieve my final goal? > > Help please! > > Best regards, > Compl > > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. > From leesteken at pm.me Thu Sep 10 15:55:17 2020 From: leesteken at pm.me (leesteken at pm.me) Date: Thu, 10 Sep 2020 15:55:17 +0000 Subject: [Haskell-cafe] Implementing parallelism using actors. How to improve the code. In-Reply-To: References: Message-ID: How about using {-# LANGUAGE TypeFamilies #-} ? class Parallel to where   type X to   type W to   par :: (z `to` X to) -> (y `to` W to) -> (z && y) `to` (X to && W to) instance Parallel (ReactiveActorBasedParallelT x w) where type X (ReactiveActorBasedParallelT x w) = x type W (ReactiveActorBasedParallelT x w) = w par z2x y2w = {- the same very long expression as before -} ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐ On Thursday, September 10, 2020 5:01 PM, Luc Duponcheel wrote: > -- Hello, I am Luc Duponcheel. > -- I started as a Haskell programmer, > -- went to Java for a living, > -- naturally evolved to Scala, > -- and, being retired now, > -- went back to my first love ... > -- I am working on a library > --       _______         __    __        _______ > --      / ___  /\       / /\  / /\      / ___  /\ > --     / /__/ / / _____/ / / / /_/__   / /__/ / / > --    / _____/ / / ___  / / / ___  /\ /____  / / > --   / /\____\/ / /__/ / / / /__/ / / \___/ / / > --  /_/ /      /______/ / /______/ /     /_/ / > --  \_\/       \______\/  \______\/      \_\/ > --                                           v1.0 > --  Program Description Based Programming Library > --  author        Luc Duponcheel       2020 - ... > -- I started writing code in Scala (presented it at several conferences). > -- I encountered issues with the fact that lazyness is not the default evaluation strategy. > -- I switched to Haskell. > -- In short: the library is about pointfree categorical programming > -- (you may wish to have a look at (https://www.youtube.com/watch?v=23VEcabMk7k (warning low sound volume)) > -- below is some code with comments (you can load the code in ghci) > -- it works fine but I want to get rid of the extra type class parameters x and w > -- I tried to get rid of x and w in two ways > -- . using forall > -- . using a GADT > -- > -- both approaches failed > -- > -- the relevant code changes that I tried are commented out-- they are underneath the code that follows > -- the code > -- specifies parallelism > -- and > -- implements it using a dummy version of the hakka library > -- (see https://hackage.haskell.org/package/hakka-0.2.0/docs/src/Hakka-Actor.html) > -- first I need some language extensions > {-# LANGUAGE TypeOperators #-} > {-# LANGUAGE MultiParamTypeClasses #-} > {-# LANGUAGE GeneralizedNewtypeDeriving #-} > {-# LANGUAGE FlexibleInstances #-} > {-# LANGUAGE InstanceSigs #-} > {-# LANGUAGE LambdaCase #-} > {-# LANGUAGE GADTs #-} > -- next I need some imports > import           Control.Monad.IO.Class > import           Control.Monad.Trans.Class > import           Control.Monad.Trans.State.Strict > import           Control.Monad.Trans.Cont > -- next some useful generic code > infixr `c` > infixr `d` > f `c` g = f . g > (f `d` g) h = f `c` g h > -- > -- parallel specification > -- > -- note the extra type class parameters x and w > -- > type z && y = (z, y) > class Parallel x w to where >     par :: (z `to` x) -> (y `to` w) -> (z && y) `to` (x && w) > -- > -- parallel implementation > -- > -- o msg stands for message > -- o mnd stands for monad > -- o rst stands for result > -- > -- > -- three actors involved > -- > -- o reactor > --  - reacts by using a continuation cont when both > --   . an x result (at left) > --   . a w result (at right) > --    have been received > -- > -- o leftActor > --  - acts to send an x result (at left) to reactor > -- > -- o rightActor > --  - acts to send a w result (at right) to reactor > -- > -- reactor sends both leftActor and rightActor a message to let them act > -- > data ActorRef = ActorRef > newtype ActorContext msg = ActorContext msg > newtype ActorT msg mnd z = ActorT { runActorT :: StateT (ActorContext msg) mnd z } >   deriving (Functor, Applicative, Monad, MonadFail, MonadTrans, MonadIO) > actor :: String -> (msg -> ActorT msg IO ()) -> ActorT msg IO ActorRef > actor name messageHandler = undefined > become :: (msg -> ActorT msg IO ()) -> ActorT msg IO () > become messageHandler = undefined > (!) :: ActorRef -> msg -> ActorT msg IO () > actorRef ! message = undefined > newtype ReactiveT msg rst mnd z = ReactiveT { runReactiveT :: ContT rst (ActorT msg mnd) z } >   deriving (Functor, Applicative, Monad, MonadFail, MonadIO) > newtype KleisliT mnd z y = KleisliT { runKleisliT :: z -> mnd y } > type ReactiveParallelT msg rst mnd = KleisliT (ReactiveT msg rst mnd) > data Message x w = LeftReact x | RightReact w | LeftAct | RightAct > type ReactiveActorBasedParallelT x w = ReactiveParallelT (Message x w) () IO > runReactiveActorBasedParallelT = runContT `d` runReactiveT `d` runKleisliT > instance Parallel x w (ReactiveActorBasedParallelT x w) where >   par z2x y2w = KleisliT >     (\case >       (z, y) -> ReactiveT >         (ContT >           (\cont -> >             actor >                 "reactor" >                 (\case >                   LeftReact x -> become >                     (\case >                       RightReact w -> cont (x, w) >                     ) >                   RightReact w -> become >                     (\case >                       LeftReact x -> cont (x, w) >                     ) >                 ) >               >>= \reactorRef -> >                     actor >                         "leftActor" >                         (\case >                           LeftAct -> runReactiveActorBasedParallelT >                             z2x >                             z >                             (\x -> reactorRef ! LeftReact x) >                         ) >                       >>= \leftActorRef -> >                             actor >                                 "rightActor" >                                 (\case >                                   RightAct -> runReactiveActorBasedParallelT >                                     y2w >                                     y >                                     (\w -> reactorRef ! RightReact w) >                                 ) >                               >>= \rightActorRef -> >                                     leftActorRef >                                       !  LeftAct >                                       >> rightActorRef >                                       !  RightAct >           ) >         ) >     ) > -- class Parallel to where > -- data Message = forall x. LeftReact x | forall w. RightReact w | LeftAct | RightAct > -- data Message where > --   LeftReact :: x -> Message > --   RightReact :: w -> Message > --   LeftAct :: Message > --   RightAct :: Message > -- type ReactiveActorBasedParallelT = ReactiveParallelT Message () IO > -- instance Parallel (ReactiveActorBasedParallelT IO) where > -- >    __~O >   -\ <, > (*)/ (*) > > reality goes far beyond imagination From luc.duponcheel at gmail.com Thu Sep 10 16:08:51 2020 From: luc.duponcheel at gmail.com (Luc Duponcheel) Date: Thu, 10 Sep 2020 18:08:51 +0200 Subject: [Haskell-cafe] Implementing parallelism using actors. How to improve the code. In-Reply-To: References: Message-ID: It works! you are my hero! On Thu, Sep 10, 2020 at 5:55 PM wrote: > How about using {-# LANGUAGE TypeFamilies #-} ? > > class Parallel to where > type X to > type W to > par :: (z `to` X to) -> (y `to` W to) -> (z && y) `to` (X to && W to) > > instance Parallel (ReactiveActorBasedParallelT x w) where > type X (ReactiveActorBasedParallelT x w) = x > type W (ReactiveActorBasedParallelT x w) = w > par z2x y2w = {- the same very long expression as before -} > > ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐ > On Thursday, September 10, 2020 5:01 PM, Luc Duponcheel < > luc.duponcheel at gmail.com> wrote: > > > -- Hello, I am Luc Duponcheel. > > -- I started as a Haskell programmer, > > -- went to Java for a living, > > -- naturally evolved to Scala, > > -- and, being retired now, > > -- went back to my first love ... > > -- I am working on a library > > -- _______ __ __ _______ > > -- / ___ /\ / /\ / /\ / ___ /\ > > -- / /__/ / / _____/ / / / /_/__ / /__/ / / > > -- / _____/ / / ___ / / / ___ /\ /____ / / > > -- / /\____\/ / /__/ / / / /__/ / / \___/ / / > > -- /_/ / /______/ / /______/ / /_/ / > > -- \_\/ \______\/ \______\/ \_\/ > > -- v1.0 > > -- Program Description Based Programming Library > > -- author Luc Duponcheel 2020 - ... > > -- I started writing code in Scala (presented it at several conferences). > > -- I encountered issues with the fact that lazyness is not the default > evaluation strategy. > > -- I switched to Haskell. > > -- In short: the library is about pointfree categorical programming > > -- (you may wish to have a look at ( > https://www.youtube.com/watch?v=23VEcabMk7k (warning low sound volume)) > > -- below is some code with comments (you can load the code in ghci) > > -- it works fine but I want to get rid of the extra type class > parameters x and w > > -- I tried to get rid of x and w in two ways > > -- . using forall > > -- . using a GADT > > -- > > -- both approaches failed > > -- > > -- the relevant code changes that I tried are commented out-- they are > underneath the code that follows > > -- the code > > -- specifies parallelism > > -- and > > -- implements it using a dummy version of the hakka library > > -- (see > https://hackage.haskell.org/package/hakka-0.2.0/docs/src/Hakka-Actor.html) > > -- first I need some language extensions > > {-# LANGUAGE TypeOperators #-} > > {-# LANGUAGE MultiParamTypeClasses #-} > > {-# LANGUAGE GeneralizedNewtypeDeriving #-} > > {-# LANGUAGE FlexibleInstances #-} > > {-# LANGUAGE InstanceSigs #-} > > {-# LANGUAGE LambdaCase #-} > > {-# LANGUAGE GADTs #-} > > -- next I need some imports > > import Control.Monad.IO.Class > > import Control.Monad.Trans.Class > > import Control.Monad.Trans.State.Strict > > import Control.Monad.Trans.Cont > > -- next some useful generic code > > infixr `c` > > infixr `d` > > f `c` g = f . g > > (f `d` g) h = f `c` g h > > -- > > -- parallel specification > > -- > > -- note the extra type class parameters x and w > > -- > > type z && y = (z, y) > > class Parallel x w to where > > par :: (z `to` x) -> (y `to` w) -> (z && y) `to` (x && w) > > -- > > -- parallel implementation > > -- > > -- o msg stands for message > > -- o mnd stands for monad > > -- o rst stands for result > > -- > > -- > > -- three actors involved > > -- > > -- o reactor > > -- - reacts by using a continuation cont when both > > -- . an x result (at left) > > -- . a w result (at right) > > -- have been received > > -- > > -- o leftActor > > -- - acts to send an x result (at left) to reactor > > -- > > -- o rightActor > > -- - acts to send a w result (at right) to reactor > > -- > > -- reactor sends both leftActor and rightActor a message to let them act > > -- > > data ActorRef = ActorRef > > newtype ActorContext msg = ActorContext msg > > newtype ActorT msg mnd z = ActorT { runActorT :: StateT (ActorContext > msg) mnd z } > > deriving (Functor, Applicative, Monad, MonadFail, MonadTrans, MonadIO) > > actor :: String -> (msg -> ActorT msg IO ()) -> ActorT msg IO ActorRef > > actor name messageHandler = undefined > > become :: (msg -> ActorT msg IO ()) -> ActorT msg IO () > > become messageHandler = undefined > > (!) :: ActorRef -> msg -> ActorT msg IO () > > actorRef ! message = undefined > > newtype ReactiveT msg rst mnd z = ReactiveT { runReactiveT :: ContT rst > (ActorT msg mnd) z } > > deriving (Functor, Applicative, Monad, MonadFail, MonadIO) > > newtype KleisliT mnd z y = KleisliT { runKleisliT :: z -> mnd y } > > type ReactiveParallelT msg rst mnd = KleisliT (ReactiveT msg rst mnd) > > data Message x w = LeftReact x | RightReact w | LeftAct | RightAct > > type ReactiveActorBasedParallelT x w = ReactiveParallelT (Message x w) > () IO > > runReactiveActorBasedParallelT = runContT `d` runReactiveT `d` > runKleisliT > > instance Parallel x w (ReactiveActorBasedParallelT x w) where > > par z2x y2w = KleisliT > > (\case > > (z, y) -> ReactiveT > > (ContT > > (\cont -> > > actor > > "reactor" > > (\case > > LeftReact x -> become > > (\case > > RightReact w -> cont (x, w) > > ) > > RightReact w -> become > > (\case > > LeftReact x -> cont (x, w) > > ) > > ) > > >>= \reactorRef -> > > actor > > "leftActor" > > (\case > > LeftAct -> runReactiveActorBasedParallelT > > z2x > > z > > (\x -> reactorRef ! LeftReact x) > > ) > > >>= \leftActorRef -> > > actor > > "rightActor" > > (\case > > RightAct -> > runReactiveActorBasedParallelT > > y2w > > y > > (\w -> reactorRef ! RightReact w) > > ) > > >>= \rightActorRef -> > > leftActorRef > > ! LeftAct > > >> rightActorRef > > ! RightAct > > ) > > ) > > ) > > -- class Parallel to where > > -- data Message = forall x. LeftReact x | forall w. RightReact w | > LeftAct | RightAct > > -- data Message where > > -- LeftReact :: x -> Message > > -- RightReact :: w -> Message > > -- LeftAct :: Message > > -- RightAct :: Message > > -- type ReactiveActorBasedParallelT = ReactiveParallelT Message () IO > > -- instance Parallel (ReactiveActorBasedParallelT IO) where > > -- > > __~O > > -\ <, > > (*)/ (*) > > > > reality goes far beyond imagination > -- __~O -\ <, (*)/ (*) reality goes far beyond imagination -------------- next part -------------- An HTML attachment was scrubbed... URL: From luc.duponcheel at gmail.com Thu Sep 10 16:19:09 2020 From: luc.duponcheel at gmail.com (Luc Duponcheel) Date: Thu, 10 Sep 2020 18:19:09 +0200 Subject: [Haskell-cafe] Need help to get started with GHC.Generics In-Reply-To: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> References: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> Message-ID: Hello Li-yao, Thanks for your reply. The code is generic in how far category theory is generic. Anyway, leesteken at pm.me solved the problem for me. Cheers Luc On Thu, Sep 10, 2020 at 5:10 PM Li-yao Xia wrote: > Hi Compl, > > I couldn't tell what's generic (in the sense of GHC.Generics) about this > example. A clearer example would be to give two applications with > different algebraic data types, and to show how they consist of the same > boilerplate, where the differences are only due to the differing numbers > of fields and constructors. > > As for tutorials on generics, a good starting point might be > generics-eot. Its documentation comes with a series of tutorials: > > https://generics-eot.readthedocs.io/en/stable/ > > Li-yao > > On 9/10/2020 9:44 AM, YueCompl via Haskell-Cafe wrote: > > Dear Cafe, > > > > I'm tinkering with the idea for arbitrary Haskell functions to be easily > > called from scripting code, I see auto derive with GHC.Generics might be > > the most promising tool, but I'm lost after read > > https://wiki.haskell.org/GHC.Generics and hackage docs. I have no clue > > so far with how to start with it. > > > > Specifically I want the section highlighted in blue get auto generated, > > within the following `runghc` ready example: > > > > ``` > > {-# LANGUAGEBangPatterns#-} > > > > moduleMain where > > > > importPrelude > > importGHC.Generics > > importData.Dynamic > > > > > > -- * minimum data structures as interface with scripting code > > > > typeAttrKey=String > > dataAttrVal=NilValue > > |IntValue!Integer > > |StrValue!String > > deriving(Eq,Ord,Typeable) > > instanceShowAttrValwhere > > show NilValue="nil" > > show (IntValue!x)=show x > > show (StrValue!x)=show x > > > > dataArgsPack=ArgsPack{ > > positional'args::[AttrVal] > > ,keyword'args::[(AttrKey,AttrVal)] > > } > > instanceSemigroupArgsPackwhere > > (ArgsPackp1 kw1)<>(ArgsPackp2 kw2)=ArgsPack(p1 ++p2)(kw1 ++kw2) > > instanceMonoidArgsPackwhere > > mempty =ArgsPack[][] > > > > classCallableawhere > > call::a->ArgsPack->(AttrVal->IO())->IO() > > > > > > -- * functions to be callable from scripting code > > > > newtypeAssert=Assert( > > Expect->MaybeTarget->Message->IOMessage > > ) > > typeExpect=AttrVal > > typeTarget=AttrVal > > typeMessage=String > > > > instanceCallableAssertwhere > > > > -- can this get auto-generated ? with > https://wiki.haskell.org/GHC.Generics > > call (Assert!assert)(ArgsPack!args !kwargs)!exit =do > > (expect,target,message)<-parseApk > > result <-assert expect target message > > exit $StrValueresult > > where > > > > parseApk::IO(Expect,MaybeTarget,Message) > > parseApk =goParse > > (Left"missing arg: expect",Nothing,Left"missing arg: message") > > args > > kwargs > > > > goParse (got'expect,got'target,got'message)[][]=casegot'expect of > > Leftmsg ->error msg > > Rightexpect ->casegot'message of > > Leftmsg ->error msg > > Rightmessage ->return (expect,got'target,message) > > goParse (got'expect,got'target,got'message)args' ((name,val):kwargs') > > =casename of > > "expect"->casegot'expect of > > Right{}->error "duplicate arg: expect" > > Left{}->goParse (Rightval,got'target,got'message)args' kwargs' > > "target"->casegot'target of > > Just{}->error "duplicate arg: target" > > Nothing->goParse (got'expect,Justval,got'message)args' kwargs' > > "message"->casegot'message of > > Right{}->error "duplicate arg: message" > > Left{}->caseval of > > StrValuemessage -> > > goParse (got'expect,got'target,Rightmessage)args' kwargs' > > _ ->error "bad arg type for: message" > > _ ->error "unexpected keyword args" > > goParse (got'expect,got'target,got'message)(val :args')[]= > > casegot'expect of > > Left{}->goParse (Rightval,got'target,got'message)args' [] > > Right{}->casegot'target of > > Nothing->goParse (got'expect,Justval,got'message)args' [] > > Just{}->casegot'message of > > Left{}->caseval of > > StrValuemessage -> > > goParse (got'expect,got'target,Rightmessage)args' [] > > _ ->error "bad arg type for: message" > > Right{}->error "extranous positional args" > > > > > > -- mockup & test out > > main::IO() > > main = > > call > > (Assertassert) > > (ArgsPack[IntValue333,StrValue"as good will"] > > [("target",IntValue333)] > > ) > > $\result ->putStrLn $"Got result: "<>show result > > > > -- | plain Haskell function meant to be easily called by scripting code > > assert::Expect->MaybeTarget->Message->IOMessage > > assert !expect !maybeTarget !message =casemaybeTarget of > > Nothing->return $"* assertion not applicable: "<>message > > Justtarget ->ifexpect ==target > > thenreturn $"* assertion passed: "<>message > > elseerror $"* assertion failed: "<>message > > > > ``` > > > > I tried to understand how > > > > * The compiler can provide a default generic implementation for > > |parseJSON > > < > https://hackage.haskell.org/package/aeson-1.5.4.0/docs/Data-Aeson.html#v:parseJSON > >|. > > > > is implemented in [aeson](https://hackage.haskell.org/package/aeson) > and > > it is overwhelming to me at the moment ... > > > > Is there easier scaffold template for me to start with GHC.Generics? Or > > there're even better techniques to achieve my final goal? > > > > Help please! > > > > Best regards, > > Compl > > > > > > _______________________________________________ > > Haskell-Cafe mailing list > > To (un)subscribe, modify options or view archives go to: > > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > > Only members subscribed via the mailman list are allowed to post. > > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -- __~O -\ <, (*)/ (*) reality goes far beyond imagination -------------- next part -------------- An HTML attachment was scrubbed... URL: From compl.yue at icloud.com Thu Sep 10 16:26:07 2020 From: compl.yue at icloud.com (YueCompl) Date: Fri, 11 Sep 2020 00:26:07 +0800 Subject: [Haskell-cafe] Need help to get started with GHC.Generics In-Reply-To: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> References: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> Message-ID: Li-yao, thanks for the pointer. And my case is not really about ADTs, but to introspect the arguments an arbitrary Haskell function takes, including how many and what type each argument is, so as to extract proper values from a given ArgsPack, then call that Haskell function with those values as args it expects. I'm not sure at a glance, that generics-eot has demonstrated how to obtain argument list with type info for a function, and will look into the details as I can. Thanks with regards, Compl > On 2020-09-10, at 23:08, Li-yao Xia wrote: > > Hi Compl, > > I couldn't tell what's generic (in the sense of GHC.Generics) about this example. A clearer example would be to give two applications with different algebraic data types, and to show how they consist of the same boilerplate, where the differences are only due to the differing numbers of fields and constructors. > > As for tutorials on generics, a good starting point might be generics-eot. Its documentation comes with a series of tutorials: > > https://generics-eot.readthedocs.io/en/stable/ > > Li-yao > > On 9/10/2020 9:44 AM, YueCompl via Haskell-Cafe wrote: >> Dear Cafe, >> I'm tinkering with the idea for arbitrary Haskell functions to be easily called from scripting code, I see auto derive with GHC.Generics might be the most promising tool, but I'm lost after read https://wiki.haskell.org/GHC.Generics and hackage docs. I have no clue so far with how to start with it. >> Specifically I want the section highlighted in blue get auto generated, within the following `runghc` ready example: >> ``` >> {-# LANGUAGEBangPatterns#-} >> moduleMain where >> importPrelude >> importGHC.Generics >> importData.Dynamic >> -- * minimum data structures as interface with scripting code >> typeAttrKey=String >> dataAttrVal=NilValue >> |IntValue!Integer >> |StrValue!String >> deriving(Eq,Ord,Typeable) >> instanceShowAttrValwhere >> show NilValue="nil" >> show (IntValue!x)=show x >> show (StrValue!x)=show x >> dataArgsPack=ArgsPack{ >> positional'args::[AttrVal] >> ,keyword'args::[(AttrKey,AttrVal)] >> } >> instanceSemigroupArgsPackwhere >> (ArgsPackp1 kw1)<>(ArgsPackp2 kw2)=ArgsPack(p1 ++p2)(kw1 ++kw2) >> instanceMonoidArgsPackwhere >> mempty =ArgsPack[][] >> classCallableawhere >> call::a->ArgsPack->(AttrVal->IO())->IO() >> -- * functions to be callable from scripting code >> newtypeAssert=Assert( >> Expect->MaybeTarget->Message->IOMessage >> ) >> typeExpect=AttrVal >> typeTarget=AttrVal >> typeMessage=String >> instanceCallableAssertwhere >> -- can this get auto-generated ? with https://wiki.haskell.org/GHC.Generics >> call (Assert!assert)(ArgsPack!args !kwargs)!exit =do >> (expect,target,message)<-parseApk >> result <-assert expect target message >> exit $StrValueresult >> where >> parseApk::IO(Expect,MaybeTarget,Message) >> parseApk =goParse >> (Left"missing arg: expect",Nothing,Left"missing arg: message") >> args >> kwargs >> goParse (got'expect,got'target,got'message)[][]=casegot'expect of >> Leftmsg ->error msg >> Rightexpect ->casegot'message of >> Leftmsg ->error msg >> Rightmessage ->return (expect,got'target,message) >> goParse (got'expect,got'target,got'message)args' ((name,val):kwargs') >> =casename of >> "expect"->casegot'expect of >> Right{}->error "duplicate arg: expect" >> Left{}->goParse (Rightval,got'target,got'message)args' kwargs' >> "target"->casegot'target of >> Just{}->error "duplicate arg: target" >> Nothing->goParse (got'expect,Justval,got'message)args' kwargs' >> "message"->casegot'message of >> Right{}->error "duplicate arg: message" >> Left{}->caseval of >> StrValuemessage -> >> goParse (got'expect,got'target,Rightmessage)args' kwargs' >> _ ->error "bad arg type for: message" >> _ ->error "unexpected keyword args" >> goParse (got'expect,got'target,got'message)(val :args')[]= >> casegot'expect of >> Left{}->goParse (Rightval,got'target,got'message)args' [] >> Right{}->casegot'target of >> Nothing->goParse (got'expect,Justval,got'message)args' [] >> Just{}->casegot'message of >> Left{}->caseval of >> StrValuemessage -> >> goParse (got'expect,got'target,Rightmessage)args' [] >> _ ->error "bad arg type for: message" >> Right{}->error "extranous positional args" >> -- mockup & test out >> main::IO() >> main = >> call >> (Assertassert) >> (ArgsPack[IntValue333,StrValue"as good will"] >> [("target",IntValue333)] >> ) >> $\result ->putStrLn $"Got result: "<>show result >> -- | plain Haskell function meant to be easily called by scripting code >> assert::Expect->MaybeTarget->Message->IOMessage >> assert !expect !maybeTarget !message =casemaybeTarget of >> Nothing->return $"* assertion not applicable: "<>message >> Justtarget ->ifexpect ==target >> thenreturn $"* assertion passed: "<>message >> elseerror $"* assertion failed: "<>message >> ``` >> I tried to understand how >> * The compiler can provide a default generic implementation for >> |parseJSON >> >|. >> is implemented in [aeson](https://hackage.haskell.org/package/aeson ) and it is overwhelming to me at the moment ... >> Is there easier scaffold template for me to start with GHC.Generics? Or there're even better techniques to achieve my final goal? >> Help please! >> Best regards, >> Compl >> _______________________________________________ >> Haskell-Cafe mailing list >> To (un)subscribe, modify options or view archives go to: >> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >> Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lysxia at gmail.com Thu Sep 10 16:35:38 2020 From: lysxia at gmail.com (Li-yao Xia) Date: Thu, 10 Sep 2020 12:35:38 -0400 Subject: [Haskell-cafe] Need help to get started with GHC.Generics In-Reply-To: References: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> Message-ID: <7ec2af6c-8f6a-a9d5-7196-822ff5e8a474@gmail.com> This doesn't sound like a use case for generics then. Just to spare you the trouble of following a red herring. On 9/10/2020 12:26 PM, YueCompl wrote: > Li-yao, thanks for the pointer. And my case is not really about ADTs, > but to introspect the arguments an arbitrary Haskell function takes, > including how many and what type each argument is, so as to extract > proper values from a given ArgsPack, then call that Haskell function > with those values as args it expects. > > I'm not sure at a glance, that generics-eot has demonstrated how to > obtain argument list with type info for a function, and will look into > the details as I can. > > Thanks with regards, > Compl > > >> On 2020-09-10, at 23:08, Li-yao Xia > > wrote: >> >> Hi Compl, >> >> I couldn't tell what's generic (in the sense of GHC.Generics) about >> this example. A clearer example would be to give two applications with >> different algebraic data types, and to show how they consist of the >> same boilerplate, where the differences are only due to the differing >> numbers of fields and constructors. >> >> As for tutorials on generics, a good starting point might be >> generics-eot. Its documentation comes with a series of tutorials: >> >> https://generics-eot.readthedocs.io/en/stable/ >> >> Li-yao >> >> On 9/10/2020 9:44 AM, YueCompl via Haskell-Cafe wrote: >>> Dear Cafe, >>> I'm tinkering with the idea for arbitrary Haskell functions to be >>> easily called from scripting code, I see auto derive >>> with GHC.Generics might be the most promising tool, but I'm lost >>> after read https://wiki.haskell.org/GHC.Generics and hackage docs. I >>> have no clue so far with how to start with it. >>> Specifically I want the section highlighted in blue get auto >>> generated, within the following `runghc` ready example: >>> ``` >>> {-# LANGUAGEBangPatterns#-} >>> moduleMain where >>> importPrelude >>> importGHC.Generics >>> importData.Dynamic >>> -- * minimum data structures as interface with scripting code >>> typeAttrKey=String >>> dataAttrVal=NilValue >>> |IntValue!Integer >>> |StrValue!String >>> deriving(Eq,Ord,Typeable) >>> instanceShowAttrValwhere >>> show NilValue="nil" >>> show (IntValue!x)=show x >>> show (StrValue!x)=show x >>> dataArgsPack=ArgsPack{ >>> positional'args::[AttrVal] >>> ,keyword'args::[(AttrKey,AttrVal)] >>> } >>> instanceSemigroupArgsPackwhere >>> (ArgsPackp1 kw1)<>(ArgsPackp2 kw2)=ArgsPack(p1 ++p2)(kw1 ++kw2) >>> instanceMonoidArgsPackwhere >>> mempty =ArgsPack[][] >>> classCallableawhere >>> call::a->ArgsPack->(AttrVal->IO())->IO() >>> -- * functions to be callable from scripting code >>> newtypeAssert=Assert( >>> Expect->MaybeTarget->Message->IOMessage >>> ) >>> typeExpect=AttrVal >>> typeTarget=AttrVal >>> typeMessage=String >>> instanceCallableAssertwhere >>> -- can this get auto-generated ? with >>> https://wiki.haskell.org/GHC.Generics >>> call (Assert!assert)(ArgsPack!args !kwargs)!exit =do >>> (expect,target,message)<-parseApk >>> result <-assert expect target message >>> exit $StrValueresult >>> where >>> parseApk::IO(Expect,MaybeTarget,Message) >>> parseApk =goParse >>> (Left"missing arg: expect",Nothing,Left"missing arg: message") >>> args >>> kwargs >>> goParse (got'expect,got'target,got'message)[][]=casegot'expect of >>> Leftmsg ->error msg >>> Rightexpect ->casegot'message of >>> Leftmsg ->error msg >>> Rightmessage ->return (expect,got'target,message) >>> goParse (got'expect,got'target,got'message)args' ((name,val):kwargs') >>> =casename of >>> "expect"->casegot'expect of >>> Right{}->error "duplicate arg: expect" >>> Left{}->goParse (Rightval,got'target,got'message)args' kwargs' >>> "target"->casegot'target of >>> Just{}->error "duplicate arg: target" >>> Nothing->goParse (got'expect,Justval,got'message)args' kwargs' >>> "message"->casegot'message of >>> Right{}->error "duplicate arg: message" >>> Left{}->caseval of >>> StrValuemessage -> >>> goParse (got'expect,got'target,Rightmessage)args' kwargs' >>> _ ->error "bad arg type for: message" >>> _ ->error "unexpected keyword args" >>> goParse (got'expect,got'target,got'message)(val :args')[]= >>> casegot'expect of >>> Left{}->goParse (Rightval,got'target,got'message)args' [] >>> Right{}->casegot'target of >>> Nothing->goParse (got'expect,Justval,got'message)args' [] >>> Just{}->casegot'message of >>> Left{}->caseval of >>> StrValuemessage -> >>> goParse (got'expect,got'target,Rightmessage)args' [] >>> _ ->error "bad arg type for: message" >>> Right{}->error "extranous positional args" >>> -- mockup & test out >>> main::IO() >>> main = >>> call >>> (Assertassert) >>> (ArgsPack[IntValue333,StrValue"as good will"] >>> [("target",IntValue333)] >>> ) >>> $\result ->putStrLn $"Got result: "<>show result >>> -- | plain Haskell function meant to be easily called by scripting code >>> assert::Expect->MaybeTarget->Message->IOMessage >>> assert !expect !maybeTarget !message =casemaybeTarget of >>> Nothing->return $"* assertion not applicable: "<>message >>> Justtarget ->ifexpect ==target >>> thenreturn $"* assertion passed: "<>message >>> elseerror $"* assertion failed: "<>message >>> ``` >>> I tried to understand how >>>  * The compiler can provide a default generic implementation for >>>    |parseJSON >>>    |. >>> is implemented in [aeson](https://hackage.haskell.org/package/aeson) >>> and it is overwhelming to me at the moment ... >>> Is there easier scaffold template for me to start with GHC.Generics? >>> Or there're even better techniques to achieve my final goal? >>> Help please! >>> Best regards, >>> Compl >>> _______________________________________________ >>> Haskell-Cafe mailing list >>> To (un)subscribe, modify options or view archives go to: >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >>> Only members subscribed via the mailman list are allowed to post. > From luc.duponcheel at gmail.com Thu Sep 10 16:40:56 2020 From: luc.duponcheel at gmail.com (Luc Duponcheel) Date: Thu, 10 Sep 2020 18:40:56 +0200 Subject: [Haskell-cafe] Need help to get started with GHC.Generics In-Reply-To: <7ec2af6c-8f6a-a9d5-7196-822ff5e8a474@gmail.com> References: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> <7ec2af6c-8f6a-a9d5-7196-822ff5e8a474@gmail.com> Message-ID: Hi Li-yao, Thanks for all your useful comments. Maybe we have different ideas about what genericity is all about. I think that my type class foe `to1 is pretty generic. And yes, it is not about ADTs. Thanks again Luc On Thu, Sep 10, 2020 at 6:38 PM Li-yao Xia wrote: > This doesn't sound like a use case for generics then. Just to spare you > the trouble of following a red herring. > > On 9/10/2020 12:26 PM, YueCompl wrote: > > Li-yao, thanks for the pointer. And my case is not really about ADTs, > > but to introspect the arguments an arbitrary Haskell function takes, > > including how many and what type each argument is, so as to extract > > proper values from a given ArgsPack, then call that Haskell function > > with those values as args it expects. > > > > I'm not sure at a glance, that generics-eot has demonstrated how to > > obtain argument list with type info for a function, and will look into > > the details as I can. > > > > Thanks with regards, > > Compl > > > > > >> On 2020-09-10, at 23:08, Li-yao Xia >> > wrote: > >> > >> Hi Compl, > >> > >> I couldn't tell what's generic (in the sense of GHC.Generics) about > >> this example. A clearer example would be to give two applications with > >> different algebraic data types, and to show how they consist of the > >> same boilerplate, where the differences are only due to the differing > >> numbers of fields and constructors. > >> > >> As for tutorials on generics, a good starting point might be > >> generics-eot. Its documentation comes with a series of tutorials: > >> > >> https://generics-eot.readthedocs.io/en/stable/ > >> > >> Li-yao > >> > >> On 9/10/2020 9:44 AM, YueCompl via Haskell-Cafe wrote: > >>> Dear Cafe, > >>> I'm tinkering with the idea for arbitrary Haskell functions to be > >>> easily called from scripting code, I see auto derive > >>> with GHC.Generics might be the most promising tool, but I'm lost > >>> after read https://wiki.haskell.org/GHC.Generics and hackage docs. I > >>> have no clue so far with how to start with it. > >>> Specifically I want the section highlighted in blue get auto > >>> generated, within the following `runghc` ready example: > >>> ``` > >>> {-# LANGUAGEBangPatterns#-} > >>> moduleMain where > >>> importPrelude > >>> importGHC.Generics > >>> importData.Dynamic > >>> -- * minimum data structures as interface with scripting code > >>> typeAttrKey=String > >>> dataAttrVal=NilValue > >>> |IntValue!Integer > >>> |StrValue!String > >>> deriving(Eq,Ord,Typeable) > >>> instanceShowAttrValwhere > >>> show NilValue="nil" > >>> show (IntValue!x)=show x > >>> show (StrValue!x)=show x > >>> dataArgsPack=ArgsPack{ > >>> positional'args::[AttrVal] > >>> ,keyword'args::[(AttrKey,AttrVal)] > >>> } > >>> instanceSemigroupArgsPackwhere > >>> (ArgsPackp1 kw1)<>(ArgsPackp2 kw2)=ArgsPack(p1 ++p2)(kw1 ++kw2) > >>> instanceMonoidArgsPackwhere > >>> mempty =ArgsPack[][] > >>> classCallableawhere > >>> call::a->ArgsPack->(AttrVal->IO())->IO() > >>> -- * functions to be callable from scripting code > >>> newtypeAssert=Assert( > >>> Expect->MaybeTarget->Message->IOMessage > >>> ) > >>> typeExpect=AttrVal > >>> typeTarget=AttrVal > >>> typeMessage=String > >>> instanceCallableAssertwhere > >>> -- can this get auto-generated ? with > >>> https://wiki.haskell.org/GHC.Generics > >>> call (Assert!assert)(ArgsPack!args !kwargs)!exit =do > >>> (expect,target,message)<-parseApk > >>> result <-assert expect target message > >>> exit $StrValueresult > >>> where > >>> parseApk::IO(Expect,MaybeTarget,Message) > >>> parseApk =goParse > >>> (Left"missing arg: expect",Nothing,Left"missing arg: message") > >>> args > >>> kwargs > >>> goParse (got'expect,got'target,got'message)[][]=casegot'expect of > >>> Leftmsg ->error msg > >>> Rightexpect ->casegot'message of > >>> Leftmsg ->error msg > >>> Rightmessage ->return (expect,got'target,message) > >>> goParse (got'expect,got'target,got'message)args' ((name,val):kwargs') > >>> =casename of > >>> "expect"->casegot'expect of > >>> Right{}->error "duplicate arg: expect" > >>> Left{}->goParse (Rightval,got'target,got'message)args' kwargs' > >>> "target"->casegot'target of > >>> Just{}->error "duplicate arg: target" > >>> Nothing->goParse (got'expect,Justval,got'message)args' kwargs' > >>> "message"->casegot'message of > >>> Right{}->error "duplicate arg: message" > >>> Left{}->caseval of > >>> StrValuemessage -> > >>> goParse (got'expect,got'target,Rightmessage)args' kwargs' > >>> _ ->error "bad arg type for: message" > >>> _ ->error "unexpected keyword args" > >>> goParse (got'expect,got'target,got'message)(val :args')[]= > >>> casegot'expect of > >>> Left{}->goParse (Rightval,got'target,got'message)args' [] > >>> Right{}->casegot'target of > >>> Nothing->goParse (got'expect,Justval,got'message)args' [] > >>> Just{}->casegot'message of > >>> Left{}->caseval of > >>> StrValuemessage -> > >>> goParse (got'expect,got'target,Rightmessage)args' [] > >>> _ ->error "bad arg type for: message" > >>> Right{}->error "extranous positional args" > >>> -- mockup & test out > >>> main::IO() > >>> main = > >>> call > >>> (Assertassert) > >>> (ArgsPack[IntValue333,StrValue"as good will"] > >>> [("target",IntValue333)] > >>> ) > >>> $\result ->putStrLn $"Got result: "<>show result > >>> -- | plain Haskell function meant to be easily called by scripting code > >>> assert::Expect->MaybeTarget->Message->IOMessage > >>> assert !expect !maybeTarget !message =casemaybeTarget of > >>> Nothing->return $"* assertion not applicable: "<>message > >>> Justtarget ->ifexpect ==target > >>> thenreturn $"* assertion passed: "<>message > >>> elseerror $"* assertion failed: "<>message > >>> ``` > >>> I tried to understand how > >>> * The compiler can provide a default generic implementation for > >>> |parseJSON > >>> < > https://hackage.haskell.org/package/aeson-1.5.4.0/docs/Data-Aeson.html#v:parseJSON > >|. > >>> is implemented in [aeson](https://hackage.haskell.org/package/aeson) > >>> and it is overwhelming to me at the moment ... > >>> Is there easier scaffold template for me to start with GHC.Generics? > >>> Or there're even better techniques to achieve my final goal? > >>> Help please! > >>> Best regards, > >>> Compl > >>> _______________________________________________ > >>> Haskell-Cafe mailing list > >>> To (un)subscribe, modify options or view archives go to: > >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > >>> Only members subscribed via the mailman list are allowed to post. > > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -- __~O -\ <, (*)/ (*) reality goes far beyond imagination -------------- next part -------------- An HTML attachment was scrubbed... URL: From compl.yue at icloud.com Thu Sep 10 16:50:30 2020 From: compl.yue at icloud.com (YueCompl) Date: Fri, 11 Sep 2020 00:50:30 +0800 Subject: [Haskell-cafe] Need help to get started with GHC.Generics In-Reply-To: <7ec2af6c-8f6a-a9d5-7196-822ff5e8a474@gmail.com> References: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> <7ec2af6c-8f6a-a9d5-7196-822ff5e8a474@gmail.com> Message-ID: Then any better approach, to auto (or at least semi-auto) adapt an ArgsPack toward applying an arbitrary Haskell function? > On 2020-09-11, at 00:35, Li-yao Xia wrote: > > This doesn't sound like a use case for generics then. Just to spare you the trouble of following a red herring. > > On 9/10/2020 12:26 PM, YueCompl wrote: >> Li-yao, thanks for the pointer. And my case is not really about ADTs, but to introspect the arguments an arbitrary Haskell function takes, including how many and what type each argument is, so as to extract proper values from a given ArgsPack, then call that Haskell function with those values as args it expects. >> I'm not sure at a glance, that generics-eot has demonstrated how to obtain argument list with type info for a function, and will look into the details as I can. >> Thanks with regards, >> Compl >>> On 2020-09-10, at 23:08, Li-yao Xia > wrote: >>> >>> Hi Compl, >>> >>> I couldn't tell what's generic (in the sense of GHC.Generics) about this example. A clearer example would be to give two applications with different algebraic data types, and to show how they consist of the same boilerplate, where the differences are only due to the differing numbers of fields and constructors. >>> >>> As for tutorials on generics, a good starting point might be generics-eot. Its documentation comes with a series of tutorials: >>> >>> https://generics-eot.readthedocs.io/en/stable/ >>> >>> Li-yao >>> >>> On 9/10/2020 9:44 AM, YueCompl via Haskell-Cafe wrote: >>>> Dear Cafe, >>>> I'm tinkering with the idea for arbitrary Haskell functions to be easily called from scripting code, I see auto derive with GHC.Generics might be the most promising tool, but I'm lost after read https://wiki.haskell.org/GHC.Generics and hackage docs. I have no clue so far with how to start with it. >>>> Specifically I want the section highlighted in blue get auto generated, within the following `runghc` ready example: >>>> ``` >>>> {-# LANGUAGEBangPatterns#-} >>>> moduleMain where >>>> importPrelude >>>> importGHC.Generics >>>> importData.Dynamic >>>> -- * minimum data structures as interface with scripting code >>>> typeAttrKey=String >>>> dataAttrVal=NilValue >>>> |IntValue!Integer >>>> |StrValue!String >>>> deriving(Eq,Ord,Typeable) >>>> instanceShowAttrValwhere >>>> show NilValue="nil" >>>> show (IntValue!x)=show x >>>> show (StrValue!x)=show x >>>> dataArgsPack=ArgsPack{ >>>> positional'args::[AttrVal] >>>> ,keyword'args::[(AttrKey,AttrVal)] >>>> } >>>> instanceSemigroupArgsPackwhere >>>> (ArgsPackp1 kw1)<>(ArgsPackp2 kw2)=ArgsPack(p1 ++p2)(kw1 ++kw2) >>>> instanceMonoidArgsPackwhere >>>> mempty =ArgsPack[][] >>>> classCallableawhere >>>> call::a->ArgsPack->(AttrVal->IO())->IO() >>>> -- * functions to be callable from scripting code >>>> newtypeAssert=Assert( >>>> Expect->MaybeTarget->Message->IOMessage >>>> ) >>>> typeExpect=AttrVal >>>> typeTarget=AttrVal >>>> typeMessage=String >>>> instanceCallableAssertwhere >>>> -- can this get auto-generated ? with https://wiki.haskell.org/GHC.Generics >>>> call (Assert!assert)(ArgsPack!args !kwargs)!exit =do >>>> (expect,target,message)<-parseApk >>>> result <-assert expect target message >>>> exit $StrValueresult >>>> where >>>> parseApk::IO(Expect,MaybeTarget,Message) >>>> parseApk =goParse >>>> (Left"missing arg: expect",Nothing,Left"missing arg: message") >>>> args >>>> kwargs >>>> goParse (got'expect,got'target,got'message)[][]=casegot'expect of >>>> Leftmsg ->error msg >>>> Rightexpect ->casegot'message of >>>> Leftmsg ->error msg >>>> Rightmessage ->return (expect,got'target,message) >>>> goParse (got'expect,got'target,got'message)args' ((name,val):kwargs') >>>> =casename of >>>> "expect"->casegot'expect of >>>> Right{}->error "duplicate arg: expect" >>>> Left{}->goParse (Rightval,got'target,got'message)args' kwargs' >>>> "target"->casegot'target of >>>> Just{}->error "duplicate arg: target" >>>> Nothing->goParse (got'expect,Justval,got'message)args' kwargs' >>>> "message"->casegot'message of >>>> Right{}->error "duplicate arg: message" >>>> Left{}->caseval of >>>> StrValuemessage -> >>>> goParse (got'expect,got'target,Rightmessage)args' kwargs' >>>> _ ->error "bad arg type for: message" >>>> _ ->error "unexpected keyword args" >>>> goParse (got'expect,got'target,got'message)(val :args')[]= >>>> casegot'expect of >>>> Left{}->goParse (Rightval,got'target,got'message)args' [] >>>> Right{}->casegot'target of >>>> Nothing->goParse (got'expect,Justval,got'message)args' [] >>>> Just{}->casegot'message of >>>> Left{}->caseval of >>>> StrValuemessage -> >>>> goParse (got'expect,got'target,Rightmessage)args' [] >>>> _ ->error "bad arg type for: message" >>>> Right{}->error "extranous positional args" >>>> -- mockup & test out >>>> main::IO() >>>> main = >>>> call >>>> (Assertassert) >>>> (ArgsPack[IntValue333,StrValue"as good will"] >>>> [("target",IntValue333)] >>>> ) >>>> $\result ->putStrLn $"Got result: "<>show result >>>> -- | plain Haskell function meant to be easily called by scripting code >>>> assert::Expect->MaybeTarget->Message->IOMessage >>>> assert !expect !maybeTarget !message =casemaybeTarget of >>>> Nothing->return $"* assertion not applicable: "<>message >>>> Justtarget ->ifexpect ==target >>>> thenreturn $"* assertion passed: "<>message >>>> elseerror $"* assertion failed: "<>message >>>> ``` >>>> I tried to understand how >>>> * The compiler can provide a default generic implementation for >>>> |parseJSON >>>> |. >>>> is implemented in [aeson](https://hackage.haskell.org/package/aeson) and it is overwhelming to me at the moment ... >>>> Is there easier scaffold template for me to start with GHC.Generics? Or there're even better techniques to achieve my final goal? >>>> Help please! >>>> Best regards, >>>> Compl >>>> _______________________________________________ >>>> Haskell-Cafe mailing list >>>> To (un)subscribe, modify options or view archives go to: >>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >>>> Only members subscribed via the mailman list are allowed to post. From lysxia at gmail.com Thu Sep 10 17:09:31 2020 From: lysxia at gmail.com (Li-yao Xia) Date: Thu, 10 Sep 2020 13:09:31 -0400 Subject: [Haskell-cafe] Need help to get started with GHC.Generics In-Reply-To: References: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> <7ec2af6c-8f6a-a9d5-7196-822ff5e8a474@gmail.com> Message-ID: <8bc73849-b0e7-c961-718a-54c57385bd22@gmail.com> Hi Compl, From my limited understanding of your problem, you might be looking for techniques revolving around "variadic functions", that is "functions with variable number of arguments". I don't have any concrete resources to point to, but it's a pretty recurrent topic of discussion. Below are two relevant Q&A on Stack Overflow to start from [1,2]; you might find especially interesting ideas from reading the implementation of Text.Printf [3] and looking for other explanations of it online. Cheers, Li-yao [1]: https://stackoverflow.com/questions/7828072/how-does-haskell-printf-work [2]: https://stackoverflow.com/questions/8353845/how-to-write-a-haskell-function-that-takes-a-variadic-function-as-an-argument [3]: http://hackage.haskell.org/package/base-4.14.0.0/docs/src/Text.Printf.html On 9/10/2020 12:50 PM, YueCompl wrote: > Then any better approach, to auto (or at least semi-auto) adapt an ArgsPack toward applying an arbitrary Haskell function? > >> On 2020-09-11, at 00:35, Li-yao Xia wrote: >> >> This doesn't sound like a use case for generics then. Just to spare you the trouble of following a red herring. >> >> On 9/10/2020 12:26 PM, YueCompl wrote: >>> Li-yao, thanks for the pointer. And my case is not really about ADTs, but to introspect the arguments an arbitrary Haskell function takes, including how many and what type each argument is, so as to extract proper values from a given ArgsPack, then call that Haskell function with those values as args it expects. >>> I'm not sure at a glance, that generics-eot has demonstrated how to obtain argument list with type info for a function, and will look into the details as I can. >>> Thanks with regards, >>> Compl >>>> On 2020-09-10, at 23:08, Li-yao Xia > wrote: >>>> >>>> Hi Compl, >>>> >>>> I couldn't tell what's generic (in the sense of GHC.Generics) about this example. A clearer example would be to give two applications with different algebraic data types, and to show how they consist of the same boilerplate, where the differences are only due to the differing numbers of fields and constructors. >>>> >>>> As for tutorials on generics, a good starting point might be generics-eot. Its documentation comes with a series of tutorials: >>>> >>>> https://generics-eot.readthedocs.io/en/stable/ >>>> >>>> Li-yao >>>> >>>> On 9/10/2020 9:44 AM, YueCompl via Haskell-Cafe wrote: >>>>> Dear Cafe, >>>>> I'm tinkering with the idea for arbitrary Haskell functions to be easily called from scripting code, I see auto derive with GHC.Generics might be the most promising tool, but I'm lost after read https://wiki.haskell.org/GHC.Generics and hackage docs. I have no clue so far with how to start with it. >>>>> Specifically I want the section highlighted in blue get auto generated, within the following `runghc` ready example: >>>>> ``` >>>>> {-# LANGUAGEBangPatterns#-} >>>>> moduleMain where >>>>> importPrelude >>>>> importGHC.Generics >>>>> importData.Dynamic >>>>> -- * minimum data structures as interface with scripting code >>>>> typeAttrKey=String >>>>> dataAttrVal=NilValue >>>>> |IntValue!Integer >>>>> |StrValue!String >>>>> deriving(Eq,Ord,Typeable) >>>>> instanceShowAttrValwhere >>>>> show NilValue="nil" >>>>> show (IntValue!x)=show x >>>>> show (StrValue!x)=show x >>>>> dataArgsPack=ArgsPack{ >>>>> positional'args::[AttrVal] >>>>> ,keyword'args::[(AttrKey,AttrVal)] >>>>> } >>>>> instanceSemigroupArgsPackwhere >>>>> (ArgsPackp1 kw1)<>(ArgsPackp2 kw2)=ArgsPack(p1 ++p2)(kw1 ++kw2) >>>>> instanceMonoidArgsPackwhere >>>>> mempty =ArgsPack[][] >>>>> classCallableawhere >>>>> call::a->ArgsPack->(AttrVal->IO())->IO() >>>>> -- * functions to be callable from scripting code >>>>> newtypeAssert=Assert( >>>>> Expect->MaybeTarget->Message->IOMessage >>>>> ) >>>>> typeExpect=AttrVal >>>>> typeTarget=AttrVal >>>>> typeMessage=String >>>>> instanceCallableAssertwhere >>>>> -- can this get auto-generated ? with https://wiki.haskell.org/GHC.Generics >>>>> call (Assert!assert)(ArgsPack!args !kwargs)!exit =do >>>>> (expect,target,message)<-parseApk >>>>> result <-assert expect target message >>>>> exit $StrValueresult >>>>> where >>>>> parseApk::IO(Expect,MaybeTarget,Message) >>>>> parseApk =goParse >>>>> (Left"missing arg: expect",Nothing,Left"missing arg: message") >>>>> args >>>>> kwargs >>>>> goParse (got'expect,got'target,got'message)[][]=casegot'expect of >>>>> Leftmsg ->error msg >>>>> Rightexpect ->casegot'message of >>>>> Leftmsg ->error msg >>>>> Rightmessage ->return (expect,got'target,message) >>>>> goParse (got'expect,got'target,got'message)args' ((name,val):kwargs') >>>>> =casename of >>>>> "expect"->casegot'expect of >>>>> Right{}->error "duplicate arg: expect" >>>>> Left{}->goParse (Rightval,got'target,got'message)args' kwargs' >>>>> "target"->casegot'target of >>>>> Just{}->error "duplicate arg: target" >>>>> Nothing->goParse (got'expect,Justval,got'message)args' kwargs' >>>>> "message"->casegot'message of >>>>> Right{}->error "duplicate arg: message" >>>>> Left{}->caseval of >>>>> StrValuemessage -> >>>>> goParse (got'expect,got'target,Rightmessage)args' kwargs' >>>>> _ ->error "bad arg type for: message" >>>>> _ ->error "unexpected keyword args" >>>>> goParse (got'expect,got'target,got'message)(val :args')[]= >>>>> casegot'expect of >>>>> Left{}->goParse (Rightval,got'target,got'message)args' [] >>>>> Right{}->casegot'target of >>>>> Nothing->goParse (got'expect,Justval,got'message)args' [] >>>>> Just{}->casegot'message of >>>>> Left{}->caseval of >>>>> StrValuemessage -> >>>>> goParse (got'expect,got'target,Rightmessage)args' [] >>>>> _ ->error "bad arg type for: message" >>>>> Right{}->error "extranous positional args" >>>>> -- mockup & test out >>>>> main::IO() >>>>> main = >>>>> call >>>>> (Assertassert) >>>>> (ArgsPack[IntValue333,StrValue"as good will"] >>>>> [("target",IntValue333)] >>>>> ) >>>>> $\result ->putStrLn $"Got result: "<>show result >>>>> -- | plain Haskell function meant to be easily called by scripting code >>>>> assert::Expect->MaybeTarget->Message->IOMessage >>>>> assert !expect !maybeTarget !message =casemaybeTarget of >>>>> Nothing->return $"* assertion not applicable: "<>message >>>>> Justtarget ->ifexpect ==target >>>>> thenreturn $"* assertion passed: "<>message >>>>> elseerror $"* assertion failed: "<>message >>>>> ``` >>>>> I tried to understand how >>>>> * The compiler can provide a default generic implementation for >>>>> |parseJSON >>>>> |. >>>>> is implemented in [aeson](https://hackage.haskell.org/package/aeson) and it is overwhelming to me at the moment ... >>>>> Is there easier scaffold template for me to start with GHC.Generics? Or there're even better techniques to achieve my final goal? >>>>> Help please! >>>>> Best regards, >>>>> Compl >>>>> _______________________________________________ >>>>> Haskell-Cafe mailing list >>>>> To (un)subscribe, modify options or view archives go to: >>>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >>>>> Only members subscribed via the mailman list are allowed to post. > From anthony_clayden at clear.net.nz Fri Sep 11 06:19:27 2020 From: anthony_clayden at clear.net.nz (Anthony Clayden) Date: Fri, 11 Sep 2020 18:19:27 +1200 Subject: [Haskell-cafe] GADT is not great - sometimes: can be almost as stupid as stupid theta Message-ID: The sweet spot for GADTs is representing ASTs for EDSLs typefully. The characteristics of those use cases is that each data constructor: carries different constraints; returns a more specific type than `T a`; similarly recursion may be to a more specific type. There's a different use case (which is I suspect what was in mind for DatatypeContexts), with characteristics for each data constructor: * the constraints are the same for all the type's parameters; * the return type is bare `T a` * any recursion is also to bare `T a` So it's the same type `a` all the way down, and therefore the same instance for the constraint(s). Consider (adapted from the H98 Report for DatatypeContexts): {-# LANGUAGE GADTs #-} data SetG a where NilSetG :: Eq a => SetG a ConsSetG :: Eq a => a -> SetG a -> SetG a sillySetG = undefined :: SetG (Int -> Int) -- accepted, but useless -- sillySetG = NilSetG :: SetG (Int -> Int) -- rejected no Eq instance (DatatypeContext's equiv with NilSet constructor at `(Int -> Int)` is accepted, so some improvement.) elemSG x NilSetG = False elemSG x (ConsSetG y ys) | x == y = True | otherwise = elemSG x ys -- ===> elemSG :: a -> SetG a -> Bool -- no Eq a inferred The elem pattern matching test makes use of ConsSetG's Eq instance, but doesn't expose that in the inferred type. Whereas: headS (ConsSetG x _) = x -- hide the pattern match tailS (ConsSetG _ xs) = xs elemSh x NilSetG = False elemSh x yss | x == headS yss = True | otherwise = elemSG x $ tailS yss -- ==> elemSh :: Eq a => a -> SetG a -> Bool -- Eq a inferred A 'morally equivalent' elem test but with a different inferred type, exposing the `Eq a` constraint. If you give an explicit signature for `elemSh`, you must specify `Eq a` -- which is annoying same as with DatatypeContexts; an explicit signature for elemSG needn't -- which gives a more annoying imprecise type that'll get reported at some usage site. I'm concluding that for this use case, a GADT doesn't 'fix' all of the stupidness with stupid theta. AntC -------------- next part -------------- An HTML attachment was scrubbed... URL: From compl.yue at icloud.com Fri Sep 11 07:18:35 2020 From: compl.yue at icloud.com (Compl Yue) Date: Fri, 11 Sep 2020 15:18:35 +0800 Subject: [Haskell-cafe] Need help to get started with GHC.Generics References: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> <7ec2af6c-8f6a-a9d5-7196-822ff5e8a474@gmail.com> <8bc73849-b0e7-c961-718a-54c57385bd22@gmail.com> Message-ID: <1599805549807.eryyrudsdya2l131hzh10rdv@android.mail.163.com> Thanks, not exactly variadic I think, and later I find that http://hackage.haskell.org/package/named seems closer to what I meant to do, only if I find a way to leverage its machinery in a programatic way.   Named parameters feels like a good practice, especially for api functions intend to be called from heterogeneous environment, I wonder why `named` is not advocated by more Haskellers so I could get to know it earlier, do the language extensions it mandates include some bad ones? At the first glance I feel my mind semanticically overloaded by its intensive use of `!` and `#`, conflicting with my intuition about bangpatterns and unboxed stuff, anyway I'm trying to grok it for sense making clicks. Cheers, Compl compl.yue 邮箱compl.yue at icloud.com 签名由 网易邮箱大师 定制 On 09/11/2020 01:09, Li-yao Xia wrote: Hi Compl, From my limited understanding of your problem, you might be looking for techniques revolving around "variadic functions", that is "functions with variable number of arguments". I don't have any concrete resources to point to, but it's a pretty recurrent topic of discussion. Below are two relevant Q&A on Stack Overflow to start from [1,2]; you might find especially interesting ideas from reading the implementation of Text.Printf [3] and looking for other explanations of it online. Cheers, Li-yao [1]: https://stackoverflow.com/questions/7828072/how-does-haskell-printf-work [2]: https://stackoverflow.com/questions/8353845/how-to-write-a-haskell-function-that-takes-a-variadic-function-as-an-argument [3]: http://hackage.haskell.org/package/base-4.14.0.0/docs/src/Text.Printf.html On 9/10/2020 12:50 PM, YueCompl wrote: > Then any better approach, to auto (or at least semi-auto) adapt an ArgsPack toward applying an arbitrary Haskell function? > >> On 2020-09-11, at 00:35, Li-yao Xia wrote: >> >> This doesn't sound like a use case for generics then. Just to spare you the trouble of following a red herring. >> >> On 9/10/2020 12:26 PM, YueCompl wrote: >>> Li-yao, thanks for the pointer. And my case is not really about ADTs, but to introspect the arguments an arbitrary Haskell function takes, including how many and what type each argument is, so as to extract proper values from a given ArgsPack, then call that Haskell function with those values as args it expects. >>> I'm not sure at a glance, that generics-eot has demonstrated how to obtain argument list with type info for a function, and will look into the details as I can. >>> Thanks with regards, >>> Compl >>>> On 2020-09-10, at 23:08, Li-yao Xia > wrote: >>>> >>>> Hi Compl, >>>> >>>> I couldn't tell what's generic (in the sense of GHC.Generics) about this example. A clearer example would be to give two applications with different algebraic data types, and to show how they consist of the same boilerplate, where the differences are only due to the differing numbers of fields and constructors. >>>> >>>> As for tutorials on generics, a good starting point might be generics-eot. Its documentation comes with a series of tutorials: >>>> >>>> https://generics-eot.readthedocs.io/en/stable/ >>>> >>>> Li-yao >>>> >>>> On 9/10/2020 9:44 AM, YueCompl via Haskell-Cafe wrote: >>>>> Dear Cafe, >>>>> I'm tinkering with the idea for arbitrary Haskell functions to be easily called from scripting code, I see auto derive with GHC.Generics might be the most promising tool, but I'm lost after read https://wiki.haskell.org/GHC.Generics and hackage docs. I have no clue so far with how to start with it. >>>>> Specifically I want the section highlighted in blue get auto generated, within the following `runghc` ready example: >>>>> ``` >>>>> {-# LANGUAGEBangPatterns#-} >>>>> moduleMain where >>>>> importPrelude >>>>> importGHC.Generics >>>>> importData.Dynamic >>>>> -- * minimum data structures as interface with scripting code >>>>> typeAttrKey=String >>>>> dataAttrVal=NilValue >>>>> |IntValue!Integer >>>>> |StrValue!String >>>>> deriving(Eq,Ord,Typeable) >>>>> instanceShowAttrValwhere >>>>> show NilValue="nil" >>>>> show (IntValue!x)=show x >>>>> show (StrValue!x)=show x >>>>> dataArgsPack=ArgsPack{ >>>>> positional'args::[AttrVal] >>>>> ,keyword'args::[(AttrKey,AttrVal)] >>>>> } >>>>> instanceSemigroupArgsPackwhere >>>>> (ArgsPackp1 kw1)<>(ArgsPackp2 kw2)=ArgsPack(p1 ++p2)(kw1 ++kw2) >>>>> instanceMonoidArgsPackwhere >>>>> mempty =ArgsPack[][] >>>>> classCallableawhere >>>>> call::a->ArgsPack->(AttrVal->IO())->IO() >>>>> -- * functions to be callable from scripting code >>>>> newtypeAssert=Assert( >>>>> Expect->MaybeTarget->Message->IOMessage >>>>> ) >>>>> typeExpect=AttrVal >>>>> typeTarget=AttrVal >>>>> typeMessage=String >>>>> instanceCallableAssertwhere >>>>> -- can this get auto-generated ? with https://wiki.haskell.org/GHC.Generics >>>>> call (Assert!assert)(ArgsPack!args !kwargs)!exit =do >>>>> (expect,target,message)<-parseApk >>>>> result <-assert expect target message >>>>> exit $StrValueresult >>>>> where >>>>> parseApk::IO(Expect,MaybeTarget,Message) >>>>> parseApk =goParse >>>>> (Left"missing arg: expect",Nothing,Left"missing arg: message") >>>>> args >>>>> kwargs >>>>> goParse (got'expect,got'target,got'message)[][]=casegot'expect of >>>>> Leftmsg ->error msg >>>>> Rightexpect ->casegot'message of >>>>> Leftmsg ->error msg >>>>> Rightmessage ->return (expect,got'target,message) >>>>> goParse (got'expect,got'target,got'message)args' ((name,val):kwargs') >>>>> =casename of >>>>> "expect"->casegot'expect of >>>>> Right{}->error "duplicate arg: expect" >>>>> Left{}->goParse (Rightval,got'target,got'message)args' kwargs' >>>>> "target"->casegot'target of >>>>> Just{}->error "duplicate arg: target" >>>>> Nothing->goParse (got'expect,Justval,got'message)args' kwargs' >>>>> "message"->casegot'message of >>>>> Right{}->error "duplicate arg: message" >>>>> Left{}->caseval of >>>>> StrValuemessage -> >>>>> goParse (got'expect,got'target,Rightmessage)args' kwargs' >>>>> _ ->error "bad arg type for: message" >>>>> _ ->error "unexpected keyword args" >>>>> goParse (got'expect,got'target,got'message)(val :args')[]= >>>>> casegot'expect of >>>>> Left{}->goParse (Rightval,got'target,got'message)args' [] >>>>> Right{}->casegot'target of >>>>> Nothing->goParse (got'expect,Justval,got'message)args' [] >>>>> Just{}->casegot'message of >>>>> Left{}->caseval of >>>>> StrValuemessage -> >>>>> goParse (got'expect,got'target,Rightmessage)args' [] >>>>> _ ->error "bad arg type for: message" >>>>> Right{}->error "extranous positional args" >>>>> -- mockup & test out >>>>> main::IO() >>>>> main = >>>>> call >>>>> (Assertassert) >>>>> (ArgsPack[IntValue333,StrValue"as good will"] >>>>> [("target",IntValue333)] >>>>> ) >>>>> $\result ->putStrLn $"Got result: "<>show result >>>>> -- | plain Haskell function meant to be easily called by scripting code >>>>> assert::Expect->MaybeTarget->Message->IOMessage >>>>> assert !expect !maybeTarget !message =casemaybeTarget of >>>>> Nothing->return $"* assertion not applicable: "<>message >>>>> Justtarget ->ifexpect ==target >>>>> thenreturn $"* assertion passed: "<>message >>>>> elseerror $"* assertion failed: "<>message >>>>> ``` >>>>> I tried to understand how >>>>>   * The compiler can provide a default generic implementation for >>>>>     |parseJSON >>>>>     |. >>>>> is implemented in [aeson](https://hackage.haskell.org/package/aeson) and it is overwhelming to me at the moment ... >>>>> Is there easier scaffold template for me to start with GHC.Generics? Or there're even better techniques to achieve my final goal? >>>>> Help please! >>>>> Best regards, >>>>> Compl >>>>> _______________________________________________ >>>>> Haskell-Cafe mailing list >>>>> To (un)subscribe, modify options or view archives go to: >>>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >>>>> Only members subscribed via the mailman list are allowed to post. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ietf-dane at dukhovni.org Fri Sep 11 08:08:54 2020 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Fri, 11 Sep 2020 04:08:54 -0400 Subject: [Haskell-cafe] GADT is not great - sometimes: can be almost as stupid as stupid theta In-Reply-To: References: Message-ID: <20200911080854.GP4758@straasha.imrryr.org> On Fri, Sep 11, 2020 at 06:19:27PM +1200, Anthony Clayden wrote: > data SetG a where > NilSetG :: Eq a => SetG a > ConsSetG :: Eq a => a -> SetG a -> SetG a > > elemSG x NilSetG = False > elemSG x (ConsSetG y ys) | x == y = True > | otherwise = elemSG x ys > -- ===> elemSG :: a -> SetG a -> Bool -- no Eq a inferred This, as I am sure you're aware, is correct, but I'd also say unsurprising. The function indeed works for all `a`, it is just that for many `a` (those without an 'Eq' instance) the type `SetG a` is uninhabited, so the function is correct vacuously. I wouldn't describe this situation as there being something "stupid" with GADTs. The function is defined for all `a` and `SetG a`, even when `SetG a` is uninhabited. Once all the constructors imply the requisite constraints, the functions are automatically safe for all, possibly uninhabited, `SetG a` types. With this particular type, I'd argue the real problem is adding the constraints to the constructors in the first place. With the constructors unconstrained, one gains the ability to define Functor instances, use Applicatives, ... And the constraints can be imposed on individual functions that actually test equality. Yes, with the deprecated `DatatypeContexts` the constraint does propagate to the use site (though one still needs to specify it explicitly, as with "unFoo" below), but there are still some anomalies: λ> :set -XFlexibleContexts λ> :set -XDatatypeContexts : warning: -XDatatypeContexts is deprecated: ... λ> data (Eq a) => Foo a = Foo a deriving Show λ> unFoo :: Eq a => Foo a -> a; unFoo (Foo a) = a λ> let x = Foo id λ> unFoo x (1 :: Int) :12:1: error: • No instance for (Eq (Int -> Int)) ... So, even with the constraint in place, we still got to define a (largely) uninhabited "x" term: x :: Eq (a -> a) => Foo (a -> a) x = Foo id Though of course one can arrange for a few special cases: instance Eq (() -> ()) where f == g = f () == g () instance Eq (Bool -> Bool) where f == g = f False == g False && f True == g True Which then allow: λ> unFoo x True True The same naturally extends to the GADT case: λ> let x = ConsSetG id NilSetG λ> elemSG (id :: Bool -> Bool) x -- Given above instance True λ> elemSG (id :: String -> String) x :39:33: error: • No instance for (Eq (String -> String)) arising from a use of ‘x’ ... -- Viktor. From anthony_clayden at clear.net.nz Fri Sep 11 11:22:26 2020 From: anthony_clayden at clear.net.nz (Anthony Clayden) Date: Fri, 11 Sep 2020 23:22:26 +1200 Subject: [Haskell-cafe] GADT is not great - sometimes: can be almost as stupid as stupid theta Message-ID: > On Fri Sep 11 08:08:54 UTC 2020, Viktor Dukhovni wrote: > This, as I am sure you're aware, is correct, Hmm. If you mean 'working as per spec' (the OutsideIn paper), then OK. > but I'd also say unsurprising. The function indeed works for all `a`, it is just that for many `a` (those without an 'Eq' instance) the type `SetG a` is uninhabited, so the function is correct vacuously. On the basis that 'well-typed programs can't go wrong'; it allows a well-typed program that crashes. That's not correct. And it's not vacuous: `elemSG` matches on constructor; the only value of `sillySG`'s type is bottom -- i.e. no constructor. I can compile sillyElem = elemSG (id :: Int -> Int) sillySetG -- inferred type Bool Why is that not ill-typed, in the absence of an instance `Eq (Int -> Int)` ? Running that crashes *** Exception: Prelude.undefined. Whereas `elemSh (id :: Int -> Int) sillySetG` is not well-typed. I could of course specify a signature for `elemSG` with explicit `Eq a =>`, but needing to do that is exactly what people complain of with DatatypeContexts. > I wouldn't describe this situation as there being something "stupid" with GADTs. To be clear: I'm not saying there's something as stupid as with DatatypeContexts. I am saying there's a family of use-cases for which GADTs are not a good fit. The example you show using DatatypeContexts fails to compile `No instance for (Eq (Int -> Int))`, which is entirely reasonable; it does compile if supplied an instance, as you go on to show. > With this particular type, I'd argue the real problem is adding the constraints to the constructors in the first place. > With the constructors unconstrained, one gains the ability to define Functor instances, use Applicatives, ... No. Missing out the constraints is a craven surrender to the limitations in Haskell. Signatures/constraints should give maximum help to the compiler to accept only well-behaved programs. That limitation is because Functor, Applicative, etc are trying to keep backwards compatibility with H98 Prelude. One approach would be for their methods to have a signature with a type family defined constraint. Or the 'Partial Type Constructors' paper shows a possible approach -- which is really orthogonal to its main topic. AntC -------------- next part -------------- An HTML attachment was scrubbed... URL: From ietf-dane at dukhovni.org Fri Sep 11 12:36:16 2020 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Fri, 11 Sep 2020 08:36:16 -0400 Subject: [Haskell-cafe] GADT is not great - sometimes: can be almost as stupid as stupid theta In-Reply-To: Message-ID: <20200911123616.GR4758@straasha.imrryr.org> On Fri, Sep 11, 2020 at 06:19:27PM +1200, Anthony Clayden wrote: > data SetG a where > NilSetG :: Eq a => SetG a > ConsSetG :: Eq a => a -> SetG a -> SetG a > > sillySetG = undefined :: SetG (Int -> Int) -- accepted, but useless > -- sillySetG = NilSetG :: SetG (Int -> Int) -- rejected no Eq instance But is it any surprise that placing bottom in an uninhabited type, and then later using it runs into a runtime crash? That seems a rather contrived problem. On Fri, Sep 11, 2020 at 11:22:26PM +1200, Anthony Clayden wrote: > > but I'd also say unsurprising. The function indeed works for all > > `a`, it is just that for many `a` (those without an 'Eq' instance) > > the type `SetG a` is uninhabited, so the function is correct > > vacuously. > > On the basis that 'well-typed programs can't go wrong'; it allows a > well-typed program that crashes. That's not correct. And it's not > vacuous: `elemSG` matches on constructor; the only value of > `sillySG`'s type is bottom -- i.e. no constructor. I can compile Well, but well typed programs do go wrong when you use `undefined`, `error`, `absurd`, 1 `div` 0, ... How is this case different? > sillyElem = elemSG (id :: Int -> Int) sillySetG -- inferred type Bool Bit sillySetG is bottom. Sure you did not get a type error, but the same thing would compile and crash even if the type were `Int` rather than `Int -> Int`, and the constraints were enforced. > Why is that not ill-typed, in the absence of an instance `Eq (Int -> Int)` ? Because the functions in question are defined for all types! Vacuously for those `a` where `SetG a` is uninhabited (by anything other than bottom. The type of `sillySetG` is only inhabited when the constraint `Eq (Int -> Int)` holds. Going outside to sound part of the type system by using bottom makes the logic unsound. Is that really a problem? > Running that crashes *** Exception: Prelude.undefined. Whereas `elemSh (id > :: Int -> Int) sillySetG` is not well-typed. But it is manifestly well-typed! We have a proof of this fact in the form of a total function definition mapping the input type to the output type for all (non-bottom) inputs. (Total functions are under no obligation to not crash on bottom). > To be clear: I'm not saying there's something as stupid as with > DatatypeContexts. I am saying there's a family of use-cases for which GADTs > are not a good fit. I don't think that Haskell offers type safety that survives placing bottom in otherwise uninhabited types and then evaluating them strictly. Non-strict evaluation works fine. The below well-typed program {-# LANGUAGE NoStrictData #-} {-# LANGUAGE GADTs #-} module Main (main) where data SetG a where NilSetG :: Eq a => SetG a ConsSetG :: Eq a => a -> SetG a -> SetG a sillySetG = undefined :: SetG (a -> a) data Y a = Y { y :: a } deriving Show lazyUpdate :: Y (SetG (Int -> Int)) -> a -> Y a lazyUpdate r a = r { y = a } defaultY :: Y (SetG (a -> a)) defaultY = Y { y = sillySetG } main :: IO () main = print $ lazyUpdate defaultY "c" prints: Y {y = "c"}. The fact that it runs to completion shows it is well typed. > The example you show using DatatypeContexts fails to compile `No instance > for (Eq (Int -> Int))`, which is entirely reasonable; it does compile if > supplied an instance, as you go on to show. Yes, you can gate the constructors, either with the deprecated DatatypeContexts, or with GADTs. But the former does not buy you any new type safety. Ill-typed programs don't compile either way. > > With this particular type, I'd argue the real problem is adding the > > constraints to the constructors in the first place. With the > > constructors unconstrained, one gains the ability to define Functor > > instances, use Applicatives, ... > > No. Missing out the constraints is a craven surrender to the limitations in > Haskell. Signatures/constraints should give maximum help to the compiler to > accept only well-behaved programs. I don't agree that your example is ill-typed. The only misbehaving term you are able to introduce is bottom, and its use surely rules out all expectations of safe behaviour. > That limitation is because Functor, Applicative, etc are trying to keep > backwards compatibility with H98 Prelude. One approach would be for their > methods to have a signature with a type family defined constraint. > > Or the 'Partial Type Constructors' paper shows a possible approach -- which > is really orthogonal to its main topic. Ah, an fmap that respects value constraints by limiting the co-domain of allowed functions is an interesting proposition. And in that (non-Haskell) case indeed the constraints would have less of a downside. Meanwhile, the GADT program *is* well typed, it just allows more uninhabited types than would be the case DatatypeContexts, but in the end the exposure to runtime errors with bottom is I think the same, (or I've not yet seen the compelling counter-example). -- Viktor. From lemming at henning-thielemann.de Fri Sep 11 16:29:26 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Fri, 11 Sep 2020 18:29:26 +0200 (CEST) Subject: [Haskell-cafe] Need help to get started with GHC.Generics In-Reply-To: <8bc73849-b0e7-c961-718a-54c57385bd22@gmail.com> References: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> <7ec2af6c-8f6a-a9d5-7196-822ff5e8a474@gmail.com> <8bc73849-b0e7-c961-718a-54c57385bd22@gmail.com> Message-ID: On Thu, 10 Sep 2020, Li-yao Xia wrote: > Hi Compl, > > From my limited understanding of your problem, you might be looking for > techniques revolving around "variadic functions", that is "functions > with variable number of arguments". I don't have any concrete resources > to point to, but it's a pretty recurrent topic of discussion. Below are > two relevant Q&A on Stack Overflow to start from [1,2]; you might find > especially interesting ideas from reading the implementation of > Text.Printf [3] and looking for other explanations of it online. Another famous variadic function is quickCheck. From anthony_clayden at clear.net.nz Sun Sep 13 07:13:11 2020 From: anthony_clayden at clear.net.nz (Anthony Clayden) Date: Sun, 13 Sep 2020 19:13:11 +1200 Subject: [Haskell-cafe] Constraints as a moveable feast? In-Reply-To: References: Message-ID: On Thu, Sep 3, 2020 at 2:18 PM Anthony Clayden wrote: > > on *Tue Sep 1 21:18:40 UTC 2020, Richard Eisenberg wrote:* > > I see the core idea as a fairly straightforward generalization of the > > "Partial Type Constructors" work -- except that I wouldn't phrase any of > > this as "floating out". > Heh heh as a wee sidenote to the idea of type synonyms/datatypes making their contexts available, as you put it | > So, the occurrence of Point a causes a Num a constraint to arise. These Num a constraints then get put into the type. The 1991 Haskell report that dropped type synonym contexts and specified datatype contexts as we now know them, left something under-specified. SPJ/GHC interpreted it one way, Mark P. Jones/Hugs interpreted differently. It didn't come to light until the H98 report: http://code.haskell.org/~dons/haskell-1990-2000/msg04066.html "Phil (Wadler), Mark (P. Jones), Jeff (Lewis)" (and Erik Meijer later in the thread) preferred Hugs's approach, so that went into the standard and into GHC. Phil said "`redundant pollution' is exactly the effect I want to achieve!". AntC -------------- next part -------------- An HTML attachment was scrubbed... URL: From kindaro at gmail.com Sun Sep 13 10:16:54 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Sun, 13 Sep 2020 15:16:54 +0500 Subject: [Haskell-cafe] Recommended reading on non-algebraic types. Message-ID: Hello Café. This is a request for reading. There is a huge literature on mathematics of program construction, but those are algebraic types therein considered; abstract types are hardly mentioned. At the same time, reality presents us with a range of compact mathematical constructions that are, however, not readily expressible via tagged unions, tuples and fixed points. A few examples: * The most usual `"containers" Data.Set (Set) ` is an abstract type that is not even lawful enough to be a functor. * It has been discovered that graphs can be represented by algebraic types _(see `alga` [1][1])_, but edge labels are, to my knowledge, not available. The types for graphs that have edge labels are all abstract. Coincidentally or not, the usual representations for the examples above are trees, refined with the use of smart constructors — so they are subtypes of algebraic types. There are less computationally accessible subtypes, say the type of prime numbers as a refinement of ℕ. So, my home baked theory is that abstract types represent subtyping in Haskell, so that every abstract type is a subtype of an algebraic type, matching a defining condition and equipped with a collection of constructors — functions whose range respects that condition. This means, for example, that: * Any projection of an algebraic type can be restricted to an abstract subtype for free. * There is a canonical partial one to one function from an algebraic type to its abstract subtype. Of course, we do not get anything like that in Haskell. Rather, we have _«abstract»_ abstract types, divorced from the underlying algebraic representation. As a consequence, we have only a handful abstract types in use, as each must be carefully crafted, verified and bundled with the totality of the interface — in a word, a bubble. By this point in my line of reasoning, I have to question if this fashion is justifiable. By now, the kind reader may concede that the matter is not without attraction. However, I am not aware of any prior art. For example, I wonder if there is a methodology for deciding if a given mathematical construction can be represented as an algebraic data type, and so far I have not seen this question being put forward. Please let me know if you have in mind some writing even faintly related to the line of inquiry presented above. [1]: https://github.com/snowleopard/alga-paper/releases/download/final/algebraic-graphs.pdf From olf at aatal-apotheke.de Sun Sep 13 21:27:59 2020 From: olf at aatal-apotheke.de (Olaf Klinke) Date: Sun, 13 Sep 2020 23:27:59 +0200 Subject: [Haskell-cafe] Recommended reading on non-algebraic types. Message-ID: <84641af3ef33bbcf676923b0a9e84385e3f5bc05.camel@aatal-apotheke.de> Dear Ignat, In the paper you linked to one should state more clearly that the two graph operations overlay and connect use an implicit equivalence relation between vertices of the two graphs involved. So these operations can be thought of as a traditional algebraic operation followed by a quotient. You might want to learn about domain semantics in general and domain environments in particular. Big names are Dana Scott and Gordon Plotkin, to name just two. Domain theory is the science of assigning (via a functor) an object of a category of partially ordered sets to each type of a (typed) lambda calculus and to each term of the calculus an arrow of that category. It turns out that algebraic domains are suitable for representing programs on algebraic data types. And if the category is just right and the lambda calculus is rich enough, then one can go the other way: Then you can prove that for each arrow in the category of domains there is a program that has that arrow as semantics. Algebraic domains match very nicely the way we build up complex data types via tuples, sums, function types and type recursion and how we build up functions via pattern matching and recursion. And every finite algebraic domain is a directed cycle-free graph. The little (free online) book by Thomas Streicher [1] might be a good start on domain semantics. A comprehensive mathematical theory of domains (not only algebraic ones) can be found in [2] but that book is expensive and hard to obtain. Sadly, with vanilla type systems such as Haskell's you can specify only algebraic domains. Anything beyond that is via "soft" definitions, meaning it can not be expressed in the type system, as you already observed. And domain theory provides mathematical proofs for this fact. Representing types like the prime numbers is possible with dependently- typed languages, but as far as I know these languages can also not go beyond algebraic domains, anyone please correct me if I'm wrong. Of course people have thought about representing other structures that are not algebraic domains. There are at least two ways I can think of: (1) What you call a sub-type: A subset of an algebraic domain. If the sub-type in question somehow sits "at the top" of the domain, then the surrounding algebraic domain is called a domain environment of the type in question. For example, the infinite binary tree is a domain environment of the type of infinite binary sequences. The important question to ask youself here is whether the sub-type is a computable one or not [*]. So if A is an algebraic data type, can you write a program that, given x :: A decides whether x is a member of the sub- type? For example, the sub-type of total functions A -> B is not a computable sub-type of the type of all functions A -> B for general A and B. (2) Quotients of algebraic domains. That means each element of your target type has several (maybe infinitely many) representations in an algebraic domain. This is fairly common. The Double data type has two representations for zero, because each Double value has a sign bit. If you want a set functor, take any container like [] or Seq and disregard multiplicities, so [x,y] and [x,y,y] represent the same set {x,y}. The art is then to write functions f that respect these quotients. Meaning if the values x and y both represent the same element in the quotient, then f(x) and f(y) represent the same element again. See section 4.2 in your linked paper on the problem of whether equivalence classes are computable. A stronger notion is a retract, that is a quotient map q :: A -> D together with a function e :: D -> A that embeds the type D back into the algebraic domain A such that q.e = id and q and e are adjoints. It is known that the retracts of algebraic domains are precisely the so- called continuous domains, a wide class that encompasses for example a real numbers object. Now you may also want to combine (1) and (2) and represent your data type as a quotient of a sub-set of an algebraic domain. As I see it your paper describes just that. It is still an open question whether certain continuous domains can be represented via expanding systems of finite domains, as is the case with algebraic domains [**]. My gut feeling is that such a description involves a description of quotients, but on another level: Since any quotient of a finite domain is algebraic, limits of such quotients will not take you beyond algebraic domains. Instead, one must describe what the quotient q :: A -> D does to the finite parts of A. Regards, Olaf [1] https://b-ok.org/book/768876/241ddb [2] Continuous lattices and domains, ISBN 0-521-80338-1 [*] One can prove that while there are uncountably many subsets of natural numbers, there are only countably many computable subsets in the sense that there exists a program that decides membership. [**] Give me any Haskell type and I show you the system of retractions of finite types whose limit is this type. From anthony_clayden at clear.net.nz Mon Sep 14 01:00:40 2020 From: anthony_clayden at clear.net.nz (Anthony Clayden) Date: Mon, 14 Sep 2020 13:00:40 +1200 Subject: [Haskell-cafe] GADT is not great - sometimes: can be almost as stupid as stupid theta In-Reply-To: References: Message-ID: On Fri, Sep 11, 2020 at 11:22 PM Anthony Clayden < anthony_clayden at clear.net.nz> wrote: > > On Fri Sep 11 08:08:54 UTC 2020, Viktor Dukhovni wrote: > > > With this particular type, I'd argue the real problem is adding the constraints > to the constructors in the first place. > > With the constructors unconstrained, one gains the ability to define > Functor instances, use Applicatives, ... > > No that isn't going to work. As Phil Wadler says here http://web.archive.org/web/20151001115936/http://code.haskell.org/~dons/haskell-1990-2000/msg04062.html what's really going on is that we want some invariant to hold over the data structure. A set's elements must be unique; so we need `Eq` to be able to test that; but `Eq` alone doesn't capture the whole invariant. So take an instance for Functor Set: `fmap` is to apply some arbitrary function to the elements of the set; there's no guarantee that the values returned will still be unique after applying the function. (Indeed there's no guarantee the function's return type is in `Eq`.) Then we need something within the overloading for `fmap` that will check uniqueness; and that'll need an `Eq` constraint. Or you decorate every call to those constructor classes/methods with post-processing to fix up the mess. Which obfuscates the logic in your elegant pointfree style. > Or the 'Partial Type Constructors' paper shows a possible approach -- > which is really orthogonal to its main topic. > > That paper picks up on a paper from John Hughes 1999, which is tackling exactly the same requirements. AntC -------------- next part -------------- An HTML attachment was scrubbed... URL: From ietf-dane at dukhovni.org Mon Sep 14 02:55:15 2020 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Sun, 13 Sep 2020 22:55:15 -0400 Subject: [Haskell-cafe] GADT is not great - sometimes: can be almost as stupid as stupid theta In-Reply-To: References: Message-ID: <20200914025515.GX4758@straasha.imrryr.org> On Mon, Sep 14, 2020 at 01:00:40PM +1200, Anthony Clayden wrote: > No that isn't going to work. As Phil Wadler says here > http://web.archive.org/web/20151001115936/http://code.haskell.org/~dons/haskell-1990-2000/msg04062.html > what's really going on is that we want some invariant to hold over the data > structure. A set's elements must be unique; so we need `Eq` to be able to > test that; but `Eq` alone doesn't capture the whole invariant. Thanks for that reference. I guess that puts retroactively on Simon's side, and perhaps in alignment with: http://web.archive.org/web/20151001182427/http://code.haskell.org/~dons/haskell-1990-2000/msg04073.html from the same thread. > So take an instance for Functor Set: `fmap` is to apply some arbitrary > function to the elements of the set; there's no guarantee that the values > returned will still be unique after applying the function. (Indeed there's > no guarantee the function's return type is in `Eq`.) Then we need something > within the overloading for `fmap` that will check uniqueness; and that'll > need an `Eq` constraint. Yes, "fmap" is not good fit for Set. And indeed with the GADT constructor constraint, there's no way to define it for non-empty sets, since the constraint cannot be met under general conditions. A restricted fmap that only allowed maps between types that support equality, could be defined, but much is lost, since Applicative would not be available, for lack of equality on arrows. With the explicit equality operator representation: data Set a = MkSet [a] (EqOper a) a function (Set a) -> (Set b) would have to provide not only a pointwise mapping, but also an explicit (EqOper b): f :: Set SomeA -> Set SomeB f (MkSet a eqA) = MkSet (g a) eqB where sanity also requires (but difficult to check in general) that: eqA a1 a2 `implies` eqB (g a1) (g a2). That is, `g` has to respect the `eqA` equivalence classes, something we'd like to expect from e.g. `Eq` instances, but again cannot in general check. -- Viktor. From johannes.waldmann at htwk-leipzig.de Mon Sep 14 15:50:31 2020 From: johannes.waldmann at htwk-leipzig.de (Johannes Waldmann) Date: Mon, 14 Sep 2020 17:50:31 +0200 Subject: [Haskell-cafe] TypeApplications and Proxy Message-ID: <7ce68432-98e1-f698-cbcb-e114c430288e@htwk-leipzig.de> Dear Cafe, since GHC has TypeApplication, we don't need Proxy that much? `natVal (Proxy :: Proxy w)` can be written as `natVal @w Proxy` but then, the argument `Proxy` looks unnecessary. I can define nat :: forall (n::Nat) . KnownNat n => Int nat = fromIntegral $ natVal @n Proxy and then use `nat @w`. (with -XAllowAmbiguousTypes.) I just wonder if that's already in some library. But then, what about the other direction (from value to type)? Is there a shorter way to write, e.g., reifyNat (read e) $ \ (_ :: Proxy w) -> run @w ... I see that Data.Reflection suggests `reifyNat (read 3) run` but then `run` needs the Proxy argument. Thanks, J.W. From lemming at henning-thielemann.de Mon Sep 14 19:23:19 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Mon, 14 Sep 2020 21:23:19 +0200 (CEST) Subject: [Haskell-cafe] TypeApplications and Proxy In-Reply-To: <7ce68432-98e1-f698-cbcb-e114c430288e@htwk-leipzig.de> References: <7ce68432-98e1-f698-cbcb-e114c430288e@htwk-leipzig.de> Message-ID: On Mon, 14 Sep 2020, Johannes Waldmann wrote: > since GHC has TypeApplication, we don't need Proxy that much? Is it as reliable as Proxy? I remember TypeApplication depends on the order in which type variables are introduced. I am afraid type applications may easily break during refactoring. But I have no experience. From johannes.waldmann at htwk-leipzig.de Mon Sep 14 19:34:13 2020 From: johannes.waldmann at htwk-leipzig.de (Johannes Waldmann) Date: Mon, 14 Sep 2020 21:34:13 +0200 Subject: [Haskell-cafe] TypeApplications and Proxy In-Reply-To: References: <7ce68432-98e1-f698-cbcb-e114c430288e@htwk-leipzig.de> Message-ID: <54857792-fb51-4d8d-b2cc-a1c4df0934aa@htwk-leipzig.de> > TypeApplication depends on the > order in which type variables are introduced. https://downloads.haskell.org/ghc/latest/docs/html/users_guide/glasgow_exts.html#ordering-of-specified-variables The easy fix is to declare all type variables. Then the first clause of 9.19.2 is enough? - J. From rae at richarde.dev Mon Sep 14 22:04:45 2020 From: rae at richarde.dev (Richard Eisenberg) Date: Mon, 14 Sep 2020 22:04:45 +0000 Subject: [Haskell-cafe] TypeApplications and Proxy In-Reply-To: <54857792-fb51-4d8d-b2cc-a1c4df0934aa@htwk-leipzig.de> References: <7ce68432-98e1-f698-cbcb-e114c430288e@htwk-leipzig.de> <54857792-fb51-4d8d-b2cc-a1c4df0934aa@htwk-leipzig.de> Message-ID: <010f01748ea61dc5-f937bfb6-a536-4a5c-bee7-a9936e226e3b-000000@us-east-2.amazonses.com> I would say that TypeApplications is stable enough, and that the ordering of type variables is reliable enough. But, sadly, we can't quite get rid of Proxy yet. Here's a very contrived example: type family F a hr :: (forall a. F a -> ()) -> () hr _ = () x = hr (\ _ -> ()) In the body of the lambda there, it is impossible to bind the type variable `a` with any Haskell construct. The workaround is to use Proxy in the type of hr. I don't know another way to do it. So I'm all for removing other uses of Proxy, but I don't think we should quite drop it from base, yet. Proposal #155 (https://github.com/ghc-proposals/ghc-proposals/pull/155) is in this area, but it's not yet implemented. Richard > On Sep 14, 2020, at 3:34 PM, Johannes Waldmann wrote: > > >> TypeApplication depends on the >> order in which type variables are introduced. > > https://downloads.haskell.org/ghc/latest/docs/html/users_guide/glasgow_exts.html#ordering-of-specified-variables > > The easy fix is to declare all type variables. > Then the first clause of 9.19.2 is enough? > > - J. > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. From lemming at henning-thielemann.de Mon Sep 14 23:41:52 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Tue, 15 Sep 2020 01:41:52 +0200 (CEST) Subject: [Haskell-cafe] TypeApplications and Proxy In-Reply-To: <54857792-fb51-4d8d-b2cc-a1c4df0934aa@htwk-leipzig.de> References: <7ce68432-98e1-f698-cbcb-e114c430288e@htwk-leipzig.de> <54857792-fb51-4d8d-b2cc-a1c4df0934aa@htwk-leipzig.de> Message-ID: On Mon, 14 Sep 2020, Johannes Waldmann wrote: >> TypeApplication depends on the >> order in which type variables are introduced. > > https://downloads.haskell.org/ghc/latest/docs/html/users_guide/glasgow_exts.html#ordering-of-specified-variables > > The easy fix is to declare all type variables. It still means, that libraries have to maintain order of type variables in order to avoid surprises in client code. Re-ordering introduction of type variables becomes a breaking change - But only if the client code chooses to use TypeApplication. From anthony_clayden at clear.net.nz Tue Sep 15 00:51:40 2020 From: anthony_clayden at clear.net.nz (Anthony Clayden) Date: Tue, 15 Sep 2020 12:51:40 +1200 Subject: [Haskell-cafe] GADT is not great - sometimes: can be almost as stupid as stupid theta In-Reply-To: References: Message-ID: On Fri, Sep 11, 2020 at 6:19 PM Anthony Clayden < anthony_clayden at clear.net.nz> wrote: > > So it's the same type `a` all the way down, and therefore the same > instance for the constraint(s). Consider (adapted from the H98 Report for > DatatypeContexts): > > {-# LANGUAGE GADTs #-} > > data SetG a where > NilSetG :: Eq a => SetG a > ConsSetG :: Eq a => a -> SetG a -> SetG a > > > elemSG x NilSetG = False > elemSG x (ConsSetG y ys) | x == y = True > | otherwise = elemSG x ys > -- ===> elemSG :: a -> SetG a -> Bool -- no Eq a inferred > > The elem pattern matching test makes use of ConsSetG's Eq instance, but > doesn't expose that in the inferred type. Whereas: > > headS (ConsSetG x _) = x -- hide the pattern match > tailS (ConsSetG _ xs) = xs > > elemSh x NilSetG = False > elemSh x yss | x == headS yss = True > | otherwise = elemSG x $ tailS yss > -- ==> elemSh :: Eq a => a -> SetG a -> Bool -- Eq a inferred > > > It seems you can get there with PatternSynonyms, using an explicit signature with 'required' constraints: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#typing-of-pattern-synonyms pattern ConsSetPS x yss = ConsSetG x yss -- inferred pattern ConsSetPS :: () => Eq a => a -> SetP a -> SetP a -- that is, if no sig given Using `ConsSetPS` as the pattern in `elemSG` with that default/inferred signature gives the same typing as using the GADT constructor. But with explicit signature pattern ConsSetPS :: Eq a => a -> SetG a -> SetG a -- longhand :: Eq a => () => a -> SetG a -> SetG a The 'required' `Eq a` is exposed, so `elemSG` gets the same signature as `elemSh` above. After all, if you're trying to maintain an invariant, you'd be using a PatternSynonym as 'smart constructor' to validate uniqueness; so giving it a signature (and hiding the GADT constructor) is easy enough. Would be nice to be able to give pattern-style signatures for GADT constructors. Unfortunately, the sigs for `ConsSetG` and `ConsSetPS` look the same but mean the opposite way round. AntC -------------- next part -------------- An HTML attachment was scrubbed... URL: From compl.yue at gmail.com Tue Sep 15 05:19:50 2020 From: compl.yue at gmail.com (Compl Yue) Date: Tue, 15 Sep 2020 13:19:50 +0800 Subject: [Haskell-cafe] TypeApplications and Proxy In-Reply-To: <7ce68432-98e1-f698-cbcb-e114c430288e@htwk-leipzig.de> References: <7ce68432-98e1-f698-cbcb-e114c430288e@htwk-leipzig.de> Message-ID: <07E4BDA4-BDA8-4058-B7B6-5C332F77956C@gmail.com> > I just wonder if that's already in some library. I happen to be learning `named`, and read here: http://hackage.haskell.org/package/named-0.3.0.1/docs/src/Named.Internal.html#local-6989586621679020197 data <>Name ( <>name :: Symbol) = <>Name <> <>instance name ~ name' => IsLabel name' (Name name ) where <>#if MIN_VERSION_base(4,10,0) fromLabel <> = Name <>#else fromLabel _ = Name <>#endif {-# INLINE fromLabel #-} <> Does this mean [IsLabel](http://hackage.haskell.org/package/base-4.12.0.0/docs/GHC-OverloadedLabels.html#t:IsLabel ) class has dropped Proxy since GHC 8.2? -------------- next part -------------- An HTML attachment was scrubbed... URL: From adam at well-typed.com Tue Sep 15 07:53:47 2020 From: adam at well-typed.com (Adam Gundry) Date: Tue, 15 Sep 2020 08:53:47 +0100 Subject: [Haskell-cafe] TypeApplications and Proxy In-Reply-To: <07E4BDA4-BDA8-4058-B7B6-5C332F77956C@gmail.com> References: <7ce68432-98e1-f698-cbcb-e114c430288e@htwk-leipzig.de> <07E4BDA4-BDA8-4058-B7B6-5C332F77956C@gmail.com> Message-ID: <76212a3a-ecec-b2d9-afe9-57ba4c60e295@well-typed.com> On 15/09/2020 06:19, Compl Yue wrote: > > Does this mean > [IsLabel](http://hackage.haskell.org/package/base-4.12.0.0/docs/GHC-OverloadedLabels.html#t:IsLabel) > class has dropped Proxy since GHC 8.2? Yes. The original implementation of IsLabel had a Proxy argument, but it was removed as unnecessary in the presence of TypeApplications [1]. As others have observed, there are certainly uses for Proxy that remain, notably to make it clear when a type variable needs to be supplied explicitly by the caller, or to allow type variables to be bound in patterns. Both of these may be alleviated by future GHC extensions, but we're not there yet. Adam [1] https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0023-overloaded-record-fields.rst#changes-to-overloadedlabels-extension -- Adam Gundry, Haskell Consultant Well-Typed LLP, https://www.well-typed.com/ Registered in England & Wales, OC335890 118 Wymering Mansions, Wymering Road, London W9 2NF, England From compl.yue at gmail.com Tue Sep 15 11:51:41 2020 From: compl.yue at gmail.com (Compl Yue) Date: Tue, 15 Sep 2020 19:51:41 +0800 Subject: [Haskell-cafe] Need help to get started with GHC.Generics In-Reply-To: References: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> <7ec2af6c-8f6a-a9d5-7196-822ff5e8a474@gmail.com> Message-ID: <9A593561-35D6-4B55-A414-21C120685836@gmail.com> An HTML attachment was scrubbed... URL: From compl.yue at gmail.com Tue Sep 15 11:55:07 2020 From: compl.yue at gmail.com (Compl Yue) Date: Tue, 15 Sep 2020 19:55:07 +0800 Subject: [Haskell-cafe] Need help to get started with GHC.Generics In-Reply-To: References: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> <7ec2af6c-8f6a-a9d5-7196-822ff5e8a474@gmail.com> Message-ID: <05092191-8A32-44A0-90F3-1F1F45F6ED0A@gmail.com> An HTML attachment was scrubbed... URL: From compl.yue at gmail.com Tue Sep 15 11:55:09 2020 From: compl.yue at gmail.com (Compl Yue) Date: Tue, 15 Sep 2020 19:55:09 +0800 Subject: [Haskell-cafe] Need help to get started with GHC.Generics In-Reply-To: References: <7c67b660-6993-47cd-0162-a6978ba8a781@gmail.com> <7ec2af6c-8f6a-a9d5-7196-822ff5e8a474@gmail.com> Message-ID: I end up with a working poc, yes, without generics involved, like this: ``` {-# LANGUAGE ViewPatterns, KindSignatures, TypeOperators, DataKinds, FlexibleInstances, FlexibleContexts, PatternSynonyms, ConstraintKinds, ScopedTypeVariables, BangPatterns #-} module Main where import Prelude import GHC.TypeLits ( Symbol , KnownSymbol , symbolVal ) import Data.Kind ( Type ) import Data.Maybe import Data.Proxy import Data.Dynamic -- artifacts for named arguments newtype NamedArg (t :: Type) (name :: Symbol) = NamedArg t type name !: t = NamedArg t name type name ?: t = NamedArg (Maybe t) name pattern Arg :: t -> name !: t pattern Arg t = NamedArg t {-# COMPLETE Arg #-} arg :: name !: t -> t arg (NamedArg a) = a optionalArg :: name ?: t -> Maybe t optionalArg (NamedArg !ma) = ma defaultArg :: t -> name ?: t -> t defaultArg !a (NamedArg !ma) = fromMaybe a ma -- * minimum data structures as interface with scripting code type AttrKey = String data AttrVal = NilValue | IntValue !Integer | StrValue !String deriving (Eq, Ord, Typeable) instance Show AttrVal where show NilValue = "nil" show (IntValue !x) = show x show (StrValue !x) = show x data ArgsPack = ArgsPack { positional'args :: [AttrVal] , keyword'args :: [(AttrKey, AttrVal)] } instance Semigroup ArgsPack where (ArgsPack p1 kw1) <> (ArgsPack p2 kw2) = ArgsPack (p1 ++ p2) (kw1 ++ kw2) instance Monoid ArgsPack where mempty = ArgsPack [] [] takeKwArg :: AttrKey -> [(AttrKey, AttrVal)] -> (Maybe AttrVal, [(AttrKey, AttrVal)]) takeKwArg !k !kwargs = go [] kwargs where go :: [(AttrKey, AttrVal)] -> [(AttrKey, AttrVal)] -> (Maybe AttrVal, [(AttrKey, AttrVal)]) go _ [] = (Nothing, kwargs) go others (p@(!key, !val) : kwargs') = if key == k then (Just val, reverse others ++ kwargs') else go (p : others) kwargs' type ContProc = (AttrVal -> IO ()) -> IO () -- | Haskell functions callable with an apk class Callable fn where call :: fn -> ArgsPack -> ContProc -- instance for nullary functions, which is the base case instance Callable ContProc where call !fn (ArgsPack !args !kwargs) exit = if null args && null kwargs then fn exit else error "extraneous args" -- instance for repacking arg receiver instance Callable fn' => Callable (ArgsPack -> fn') where call !fn !apk !exit = call (fn apk) (ArgsPack [] []) exit -- instances for positional arg receivers instance Callable fn' => Callable (AttrVal -> fn') where call !fn (ArgsPack (val : args) !kwargs) !exit = call (fn val) (ArgsPack args kwargs) exit call _ _ _ = error "missing anonymous arg" instance Callable fn' => Callable (Maybe AttrVal -> fn') where call !fn (ArgsPack [] !kwargs) !exit = call (fn Nothing) (ArgsPack [] kwargs) exit call !fn (ArgsPack (val : args) !kwargs) !exit = call (fn (Just val)) (ArgsPack args kwargs) exit instance Callable fn' => Callable (String -> fn') where call !fn (ArgsPack (val : args) !kwargs) !exit = case val of StrValue !val' -> call (fn val') (ArgsPack args kwargs) exit _ -> error "arg type mismatch" call _ _ _ = error "missing anonymous arg" instance Callable fn' => Callable (Maybe String -> fn') where call !fn (ArgsPack [] !kwargs) !exit = call (fn Nothing) (ArgsPack [] kwargs) exit call !fn (ArgsPack (val : args) !kwargs) !exit = case val of StrValue !val' -> call (fn (Just val')) (ArgsPack args kwargs) exit _ -> error "arg type mismatch" -- todo instances for receivers of positional arg of (Maybe) Integer -- type, and other types covered by AttrVal -- instances for keyword arg receivers instance (KnownSymbol name, Callable fn') => Callable (NamedArg AttrVal name -> fn') where call !fn (ArgsPack !args !kwargs) !exit = case takeKwArg argName kwargs of (Just !val, kwargs') -> call (fn (NamedArg val)) (ArgsPack args kwargs') exit (Nothing, kwargs') -> case args of [] -> error $ "missing named arg: " <> argName (val : args') -> call (fn (NamedArg val)) (ArgsPack args' kwargs') exit where !argName = symbolVal (Proxy :: Proxy name) instance (KnownSymbol name, Callable fn') => Callable (NamedArg (Maybe AttrVal) name -> fn') where call !fn (ArgsPack !args !kwargs) !exit = case takeKwArg argName kwargs of (Nothing, !kwargs') -> case args of [] -> call (fn (NamedArg Nothing)) (ArgsPack [] kwargs') exit val : args' -> call (fn (NamedArg (Just val))) (ArgsPack args' kwargs') exit (!maybeVal, !kwargs') -> call (fn (NamedArg maybeVal)) (ArgsPack args kwargs') exit where !argName = symbolVal (Proxy :: Proxy name) instance (KnownSymbol name, Callable fn') => Callable (NamedArg String name -> fn') where call !fn (ArgsPack !args !kwargs) !exit = case takeKwArg argName kwargs of (Just !val, !kwargs') -> case val of StrValue !val' -> call (fn (NamedArg val')) (ArgsPack args kwargs') exit _ -> error "arg type mismatch" (Nothing, !kwargs') -> case args of [] -> error $ "missing named arg: " <> argName val : args' -> case val of StrValue !val' -> call (fn (NamedArg val')) (ArgsPack args' kwargs') exit _ -> error "arg type mismatch" where !argName = symbolVal (Proxy :: Proxy name) instance (KnownSymbol name, Callable fn') => Callable (NamedArg (Maybe String) name -> fn') where call !fn (ArgsPack !args !kwargs) !exit = case takeKwArg argName kwargs of (Just !val, !kwargs') -> case val of StrValue !val' -> call (fn (NamedArg (Just val'))) (ArgsPack args kwargs') exit _ -> error "arg type mismatch" (Nothing, !kwargs') -> case args of [] -> call (fn (NamedArg Nothing)) (ArgsPack [] kwargs') exit val : args' -> case val of StrValue !val' -> call (fn (NamedArg (Just val'))) (ArgsPack args' kwargs') exit _ -> error "arg type mismatch" where !argName = symbolVal (Proxy :: Proxy name) -- todo instances for receivers of keyword arg of (Maybe) Integer -- type, and other types covered by AttrVal -- * functions to be callable from scripting code -- | interfacing Haskell function meant to be easily called by scripting code assert :: "expect" !: AttrVal -> "target" ?: AttrVal -> "message" ?: String -> (AttrVal -> IO ()) -> IO () assert (Arg !expect) (optionalArg -> !maybeTarget) (defaultArg "sth ought to be" -> !message) !exit = case maybeTarget of Nothing -> case expect of NilValue -> error $ "* assertion failed: " <> message IntValue 0 -> error $ "* assertion failed: " <> message StrValue "" -> error $ "* assertion failed: " <> message _ -> exit $ StrValue $ "* assertion passed: " <> message Just target -> if expect == target then exit $ StrValue $ "* assertion passed: " <> message else error $ "* assertion failed: " <> message -- mockup & test out main :: IO () main = do call assert apk1 $ \ !result -> putStrLn $ "Got result1: " <> show result call assert apk2 $ \ !result -> putStrLn $ "Got result2: " <> show result call assert apk3 $ \ !result -> putStrLn $ "Got result3: " <> show result call assert apk4 $ \ !result -> putStrLn $ "Got result4: " <> show result where !apk1 = ArgsPack [] [ ("message", StrValue "as good will") , ("target" , IntValue 333) , ("expect" , IntValue 333) ] !apk2 = ArgsPack [IntValue 333, IntValue 333, StrValue "as good will"] [] !apk3 = ArgsPack [IntValue 333] [("target", IntValue 333)] !apk4 = ArgsPack [] [("target", IntValue 333), ("expect", IntValue 555)] ``` > On 2020-09-11, at 00:50, YueCompl via Haskell-Cafe > wrote: > > Then any better approach, to auto (or at least semi-auto) adapt an ArgsPack toward applying an arbitrary Haskell function? > >> On 2020-09-11, at 00:35, Li-yao Xia > wrote: >> >> This doesn't sound like a use case for generics then. Just to spare you the trouble of following a red herring. >> >> On 9/10/2020 12:26 PM, YueCompl wrote: >>> Li-yao, thanks for the pointer. And my case is not really about ADTs, but to introspect the arguments an arbitrary Haskell function takes, including how many and what type each argument is, so as to extract proper values from a given ArgsPack, then call that Haskell function with those values as args it expects. >>> I'm not sure at a glance, that generics-eot has demonstrated how to obtain argument list with type info for a function, and will look into the details as I can. >>> Thanks with regards, >>> Compl >>>> On 2020-09-10, at 23:08, Li-yao Xia >> wrote: >>>> >>>> Hi Compl, >>>> >>>> I couldn't tell what's generic (in the sense of GHC.Generics) about this example. A clearer example would be to give two applications with different algebraic data types, and to show how they consist of the same boilerplate, where the differences are only due to the differing numbers of fields and constructors. >>>> >>>> As for tutorials on generics, a good starting point might be generics-eot. Its documentation comes with a series of tutorials: >>>> >>>> https://generics-eot.readthedocs.io/en/stable/ >>>> >>>> Li-yao >>>> >>>> On 9/10/2020 9:44 AM, YueCompl via Haskell-Cafe wrote: >>>>> Dear Cafe, >>>>> I'm tinkering with the idea for arbitrary Haskell functions to be easily called from scripting code, I see auto derive with GHC.Generics might be the most promising tool, but I'm lost after read https://wiki.haskell.org/GHC.Generics and hackage docs. I have no clue so far with how to start with it. >>>>> Specifically I want the section highlighted in blue get auto generated, within the following `runghc` ready example: >>>>> ``` >>>>> {-# LANGUAGEBangPatterns#-} >>>>> moduleMain where >>>>> importPrelude >>>>> importGHC.Generics >>>>> importData.Dynamic >>>>> -- * minimum data structures as interface with scripting code >>>>> typeAttrKey=String >>>>> dataAttrVal=NilValue >>>>> |IntValue!Integer >>>>> |StrValue!String >>>>> deriving(Eq,Ord,Typeable) >>>>> instanceShowAttrValwhere >>>>> show NilValue="nil" >>>>> show (IntValue!x)=show x >>>>> show (StrValue!x)=show x >>>>> dataArgsPack=ArgsPack{ >>>>> positional'args::[AttrVal] >>>>> ,keyword'args::[(AttrKey,AttrVal)] >>>>> } >>>>> instanceSemigroupArgsPackwhere >>>>> (ArgsPackp1 kw1)<>(ArgsPackp2 kw2)=ArgsPack(p1 ++p2)(kw1 ++kw2) >>>>> instanceMonoidArgsPackwhere >>>>> mempty =ArgsPack[][] >>>>> classCallableawhere >>>>> call::a->ArgsPack->(AttrVal->IO())->IO() >>>>> -- * functions to be callable from scripting code >>>>> newtypeAssert=Assert( >>>>> Expect->MaybeTarget->Message->IOMessage >>>>> ) >>>>> typeExpect=AttrVal >>>>> typeTarget=AttrVal >>>>> typeMessage=String >>>>> instanceCallableAssertwhere >>>>> -- can this get auto-generated ? with https://wiki.haskell.org/GHC.Generics >>>>> call (Assert!assert)(ArgsPack!args !kwargs)!exit =do >>>>> (expect,target,message)<-parseApk >>>>> result <-assert expect target message >>>>> exit $StrValueresult >>>>> where >>>>> parseApk::IO(Expect,MaybeTarget,Message) >>>>> parseApk =goParse >>>>> (Left"missing arg: expect",Nothing,Left"missing arg: message") >>>>> args >>>>> kwargs >>>>> goParse (got'expect,got'target,got'message)[][]=casegot'expect of >>>>> Leftmsg ->error msg >>>>> Rightexpect ->casegot'message of >>>>> Leftmsg ->error msg >>>>> Rightmessage ->return (expect,got'target,message) >>>>> goParse (got'expect,got'target,got'message)args' ((name,val):kwargs') >>>>> =casename of >>>>> "expect"->casegot'expect of >>>>> Right{}->error "duplicate arg: expect" >>>>> Left{}->goParse (Rightval,got'target,got'message)args' kwargs' >>>>> "target"->casegot'target of >>>>> Just{}->error "duplicate arg: target" >>>>> Nothing->goParse (got'expect,Justval,got'message)args' kwargs' >>>>> "message"->casegot'message of >>>>> Right{}->error "duplicate arg: message" >>>>> Left{}->caseval of >>>>> StrValuemessage -> >>>>> goParse (got'expect,got'target,Rightmessage)args' kwargs' >>>>> _ ->error "bad arg type for: message" >>>>> _ ->error "unexpected keyword args" >>>>> goParse (got'expect,got'target,got'message)(val :args')[]= >>>>> casegot'expect of >>>>> Left{}->goParse (Rightval,got'target,got'message)args' [] >>>>> Right{}->casegot'target of >>>>> Nothing->goParse (got'expect,Justval,got'message)args' [] >>>>> Just{}->casegot'message of >>>>> Left{}->caseval of >>>>> StrValuemessage -> >>>>> goParse (got'expect,got'target,Rightmessage)args' [] >>>>> _ ->error "bad arg type for: message" >>>>> Right{}->error "extranous positional args" >>>>> -- mockup & test out >>>>> main::IO() >>>>> main = >>>>> call >>>>> (Assertassert) >>>>> (ArgsPack[IntValue333,StrValue"as good will"] >>>>> [("target",IntValue333)] >>>>> ) >>>>> $\result ->putStrLn $"Got result: "<>show result >>>>> -- | plain Haskell function meant to be easily called by scripting code >>>>> assert::Expect->MaybeTarget->Message->IOMessage >>>>> assert !expect !maybeTarget !message =casemaybeTarget of >>>>> Nothing->return $"* assertion not applicable: "<>message >>>>> Justtarget ->ifexpect ==target >>>>> thenreturn $"* assertion passed: "<>message >>>>> elseerror $"* assertion failed: "<>message >>>>> ``` >>>>> I tried to understand how >>>>> * The compiler can provide a default generic implementation for >>>>> |parseJSON >>>>> >|. >>>>> is implemented in [aeson](https://hackage.haskell.org/package/aeson ) and it is overwhelming to me at the moment ... >>>>> Is there easier scaffold template for me to start with GHC.Generics? Or there're even better techniques to achieve my final goal? >>>>> Help please! >>>>> Best regards, >>>>> Compl >>>>> _______________________________________________ >>>>> Haskell-Cafe mailing list >>>>> To (un)subscribe, modify options or view archives go to: >>>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >>>>> Only members subscribed via the mailman list are allowed to post. > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From anthony_clayden at clear.net.nz Tue Sep 15 12:57:18 2020 From: anthony_clayden at clear.net.nz (Anthony Clayden) Date: Wed, 16 Sep 2020 00:57:18 +1200 Subject: [Haskell-cafe] Design for DatatypeContexts: what was the idea, anyway? Message-ID: Quite prosaically: the design for DatatypeContexts seems to have materialised with no visible discussion, neither agreement nor disagreement; and persisted almost unchanged until they got deprecated nearly 20 years later. I can guess there was a verbal discussion amongst the Committee circa 1990/91; that somebody was deputed to write down the conclusion (memo March 1991 [1]) ; and the implementers (both Committee members) went to work in Hugs and GHC. Much later (1999, while preparing the Haskell 1998 Report, published 2002) a fresh set of eyes spotted an under-explicitness that had been interpreted differently by the implementers; and there was a Committee thread resolving it. That thread is the best place from which to understand the intent, particularly Phil Wadler's comments [2]. Historical context: typeclasses/constraints/contexts were the new shiny thing, still rapidly evolving (Wadler early 1989, Wadler & Blott late 1989 but with several open questions). They were initially tied to overloading methods; by 1990 the Haskell report v1.0 included constraints for Numeric Literals, but with several caveats. The mailing lists had much back-and-forth about ambiguities and defaulting and (of course) the Dreaded Monomorphism Restriction for constrained terms. I see comments about the "dictionary transfer problem" and dictionaries "shared lazily between all applications to each elt [in a data structure]". The syntax of prefixing the context to the type constructor name (syntax in the 1990 Haskell Report, along with the admittedly vague descriptions there), suggests to me they were thought of as for numeric literals: `5 :: Num a => a` so `(ConsSet 5 NilSet) :: Num a => Set a`, where Set's `Eq a` is implied, as a superclass of Num. There's no visible method invocation. If the intent had been that the context attached only inside the data constructors, I'd expect syntax like one of these: data Set a = NilSet | (Eq a) => ConsSet a (Set a) data Set a = NilSet | ConsSet (Eq a) => a (Set a) (Syntax closer to that second did arrive later with GADTs.) The 1999 discussion concluded that the context should be exposed outside of a pattern match, even though a match only 'consumes' an already constructed value. Exposing makes sense if all data constructors enforce the context, but from the 1991 design, `NilSet`'s type doesn't mention the context (because there's no argument to the constructor to ground the type?). I'm confused by that: if Haskell 1990 supported `5 :: Num a => a`, what's wrong with `NilSet :: Eq a => Set a`? I sense quite a bit of exasperation in that 1991 memo: "Nobody has been able to give a satisfactory account", "Nobody has been able to explain", several phrases in block caps. The so-called "simplifications" are almost what Parliamentarians would call a 'wrecking amendment' (undermining or nullifying the intent of a proposal) or even applying Cunningham's Law (the quickest way to get the right answer is not to ask a question, but to post a wrong answer) -- except nobody (visibly) pushed back. And there's hints of dissent unresolved up to the 1999 exchange: "a broken feature of H98", "What you propose, I think, offers the worst of both worlds ", "a compile-time language feature can be hard to understand if it is not directly tied to run-time behaviour.", "Live and learn.". (Who should learn? Is that a self-admonition or aimed at others?) It's well known that SPJ dubbed the feature "stupid theta". The thing is: the scribe in 1991 was SPJ; even if he was only writing from dictation, why specify and then implement a "broken feature"? Or did it only earn the "stupid" as a result of the 1999 decision? Then it's notable in that decision that Hugs' behaviour was preferred; GHC was changed to align; but then GADTs design reverted to the GHC pre-1999 behaviour (which is reasonable considering different constructors have different constraints and return types). Pattern Synonyms support double-contexts in the signature, to be explicit about what's exposed/required outside a pattern match. I see much recent traffic (say on StackOverflow) that GADTs are DatatypeContexts 'done right'. I can't agree with that: GADTs are addressing a different set of use cases vs. the motivation Wadler explains in 1999 -- to enforce invariants -- which makes sense to me. AntC [1] http://web.archive.org/web/20160320062120/http://code.haskell.org/~dons/haskell-1990-2000/msg00072.html [2] http://web.archive.org/web/20151001115936/http://code.haskell.org/~dons/haskell-1990-2000/msg04062.html (The question is posed in the preceding message in the thread; follow the thread through to see the number of luminaries disagreeing with GHC's then behaviour.) -------------- next part -------------- An HTML attachment was scrubbed... URL: From kindaro at gmail.com Wed Sep 16 17:21:13 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Wed, 16 Sep 2020 22:21:13 +0500 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? Message-ID: I apologize if this topic has already been clarified elsewhere — please refer me. The present message can also be seen _(and answered)_ on the Haskell Discourse board.[1] To clarify, an explicit export: module X (…) where … An explicit import: import X (…) ## I would like to question the desirability of explicit exports and local imports in a production application. I want to make two cases: * That explicit exports are never necessary. Moreover, that explicit exports are harmful as they make checking of non-exported definitions impossible. * That explicit imports are not necessary when importing a module from the same package. By default, I make an easy claim that none are desirable since they incur mindless typing effort and thereby unfairly tax the programmer. Let us then consider the justifications for their use despite that. ### Explicit exports. When there are no explicit exports, everything is exported from a module. Recall a case when this is disadvantageous: abstract types. The values of abstract types must only be obtained via smart constructors. So their definitions should not be made available. Explicit exports may provide that. However, there is another practice: putting dangerous definitions of the supposedly abstract type `X` into a module `X.Internal`, then importing them to module `X` and defining smart constructors there. By default, a module only exports the definitions defined in itself — there are no re-exports. So, a user importing `X` cannot invoke unsafe constructors from `X.Internal`, and they know better than to import the dangerous module directly. In the former case above, the internal definitions remain out of reach for a test suite. In the latter case, they may be freely checked as needed. ### Explicit imports. Recall the scenario in which explicit imports are useful. * A module `"p" X` from package `p` imports a module `"q" Y` from package `q v0.0.0`. * The package `q` bumps the third version number and exports a definition `Y.d` which name coincides with an already defined definition `X.d`. What happens? * If imports are explicit, nothing happens. The maintainers of `p` are safe if they set a restriction as weak as `q ^>= 0.0`. * If imports are implicit, there would be a clash of names if `p` is built against `q v0.0.1`, but the build would pass if it is built against a less recent version `q v0.0.0.1`. The maintainers of `p` might not even notice that the package does not build in some cases. When they do, they would have to set a restriction `q ^>= 0.0.0` But surely there is only one version to build against if the two modules reside in the same package. So the overlapping names will necessarily result in an error that would immediately be rectified. It is no different from any other compile time error. ## Are my considerations fair? [1]: https://discourse.haskell.org/t/are-explicit-exports-and-local-imports-desirable-in-a-production-application/1411 From lemming at henning-thielemann.de Wed Sep 16 17:36:35 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Wed, 16 Sep 2020 19:36:35 +0200 (CEST) Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: Message-ID: On Wed, 16 Sep 2020, Ignat Insarov wrote: > ### Explicit exports. > > When there are no explicit exports, everything is exported from a > module. Recall a case when this is disadvantageous: abstract types. > The values of abstract types must only be obtained via smart > constructors. So their definitions should not be made available. > Explicit exports may provide that. You can do the following in Cabal: Library private Exposed-Modules: A.Internal Library Exposed-Modules: A Build-Depends: private Test-Suite foobar-test Build-Depends: private This way you can export the constructors of a datatype from A.Internal and use them in A and in the test-suite, but the user of your package cannot access them. > ### Explicit imports. > > Recall the scenario in which explicit imports are useful. > > * A module `"p" X` from package `p` imports a module `"q" Y` from > package `q v0.0.0`. > * The package `q` bumps the third version number and exports a > definition `Y.d` which name coincides with an already defined > definition `X.d`. I would not distinguish between imports between modules from the same and from other packages, because in the course of refactoring you might want to split a package. Instead I write all my modules with qualified imports in mind, such that I can write e.g. Window.open instead of openWindow. E.g. 'containers' is a good pattern to follow. From kindaro at gmail.com Wed Sep 16 17:52:23 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Wed, 16 Sep 2020 22:52:23 +0500 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: Message-ID: Thank you Henning. So, the notion of internal libraries amounts to an even stronger argument in favour of eschewing explicit exports, at least in the case we consider. Am I reading your implication correctly? As for qualified imports, I also follow this practice and I think it is most readable, given that the qualifications are words and not just letters — alas, using single letter qualifications, such as `T.pack` for `Data.Text.pack`, is common. From lemming at henning-thielemann.de Wed Sep 16 17:56:47 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Wed, 16 Sep 2020 19:56:47 +0200 (CEST) Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: Message-ID: On Wed, 16 Sep 2020, Ignat Insarov wrote: > So, the notion of internal libraries amounts to an even stronger > argument in favour of eschewing explicit exports, at least in the case > we consider. Am I reading your implication correctly? You need explicit exports in the public interface, in my example module "A". > As for qualified imports, I also follow this practice and I think it > is most readable, given that the qualifications are words and not just > letters — alas, using single letter qualifications, such as `T.pack` > for `Data.Text.pack`, is common. Yes, I prefer Text.pack and Map.union etc. From kindaro at gmail.com Wed Sep 16 18:27:30 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Wed, 16 Sep 2020 23:27:30 +0500 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: Message-ID: > You need explicit exports in the public interface, in my example module > "A". How so? Consider an example [1]. Building it yields an error, showing that any unsafe definitions contained in `A.Internal` are out of reach from another package. I must be missing your point. [1]: https://github.com/kindaro/exports-and-internal-libraries From monkleyon at gmail.com Wed Sep 16 20:40:37 2020 From: monkleyon at gmail.com (MarLinn) Date: Wed, 16 Sep 2020 22:40:37 +0200 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: Message-ID: <0a15a1cc-6daf-6fd1-d1c9-ff19c588edb1@gmail.com> > * That explicit exports are never necessary. Moreover, that explicit > exports are harmful as they make checking of non-exported definitions > impossible. It's not just the internal API in the form of constructors that's worth hiding. What about helper functions? The ones that would be "private" in an OOP language? Should I rip my logical structure apart just because exports are tedious? In fact the OOP separation is a decent framework to reference for perspective: public → (explicitly) exported private → not exported at all package protected → exported from internal module protected → not directly controlled by exports, but by types and available construction paths All have an equivalent, all are necessary. But I agree that explicit exports are flawed, because the vast majority of "stuff" is usually exported. A better way might be to export everything except explicitly hidden stuff. That's not possible right now (I think), but imagine syntax like this:     module Foo (..) hiding ( bar, baz ) where or     module Foo (module Foo hiding ( bar, baz )) where From lemming at henning-thielemann.de Wed Sep 16 20:55:35 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Wed, 16 Sep 2020 22:55:35 +0200 (CEST) Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: <0a15a1cc-6daf-6fd1-d1c9-ff19c588edb1@gmail.com> References: <0a15a1cc-6daf-6fd1-d1c9-ff19c588edb1@gmail.com> Message-ID: On Wed, 16 Sep 2020, MarLinn wrote: > But I agree that explicit exports are flawed, because the vast majority > of "stuff" is usually exported. A better way might be to export > everything except explicitly hidden stuff. That's not possible right now > (I think), but imagine syntax like this: > >     module Foo (..) hiding ( bar, baz ) where > > or > >     module Foo (module Foo hiding ( bar, baz )) where In Oberon you do not specify an export list, instead you mark exported identifiers with a star. This way you avoid duplication of identifiers in the export list and the identifier order is always the order of definitions in the module. From lemming at henning-thielemann.de Wed Sep 16 21:01:31 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Wed, 16 Sep 2020 23:01:31 +0200 (CEST) Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: Message-ID: On Wed, 16 Sep 2020, Ignat Insarov wrote: >> You need explicit exports in the public interface, in my example module >> "A". > > How so? My example should be like so: module A.Internal where data T = Private Integer module A ( T, -- do not export constructor f, ) where import A.Internal (T(Private)) f :: FooBar f = do something with Private You would need the export list in A for exposing T, but not Private. From emilypi at cohomolo.gy Thu Sep 17 03:44:23 2020 From: emilypi at cohomolo.gy (Emily Pillmore) Date: Thu, 17 Sep 2020 03:44:23 +0000 Subject: [Haskell-cafe] [ANN] base16-bytestring-1.0.0.0 Message-ID: Hello Everyone, I'm pleased to announce version 1.0.0.0 of the `base16-bytestring` library. This release marks an epochal change in the library's structure, changing some existing features, and adding others. There was only on PR contribution marking this release, but it was a big omnibus one. In the latest release, we... * Improved performance by 3-4x for encode, 4-5x for decode. * Changed the ` decode` signature to return an error message with offset. The signature will now be ` ByteString -> Either String ByteString` in alignment with other encoding standards. * Actually test using the test vectors defined in the RFC, and uses property tests to ensure invariants hold. * Added lenient decoders to the API * Added ` -XTrustworthy` annotations to the relevant exposed modules * Rewrote the haddocks to be more up to date and fancy-styled. * Added benchmarks to the `.cabal` file as opposed to being a separate target, so they can be run at toplevel, and can exist as an integral part of the spec. * Bumped the Cabal version to 1.12 Thanks to Herbert and Mikhail for helping make this happen. Cheers, Emily -------------- next part -------------- An HTML attachment was scrubbed... URL: From olf at aatal-apotheke.de Thu Sep 17 17:53:11 2020 From: olf at aatal-apotheke.de (Olaf Klinke) Date: Thu, 17 Sep 2020 19:53:11 +0200 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? Message-ID: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> Dear Ignat, have you seen https://wiki.haskell.org/Import_modules_properly https://wiki.haskell.org/Qualified_names I find the arguments convincing. Even in my own packages I sometimes get lost where a certain function was imported from. When neither exports nor imports are done explicitly, you usually have only two choices: 1. search all sources (e.g. with grep -l) 2. rely on the haddock index Maybe your IDE can do that for you, but you can't expect all downstream users or all your colleagues to do the same. -- Olaf From godzbanebane at gmail.com Thu Sep 17 18:05:54 2020 From: godzbanebane at gmail.com (Georgi Lyubenov) Date: Thu, 17 Sep 2020 21:05:54 +0300 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> References: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> Message-ID: Hard +1 to what Olaf said - this was (still is if I can't get ghcide running) one of the most annoying things when exploring a new codebase for me. ====== Georgi -------------- next part -------------- An HTML attachment was scrubbed... URL: From isaace71295 at gmail.com Thu Sep 17 22:06:14 2020 From: isaace71295 at gmail.com (Isaac Elliott) Date: Fri, 18 Sep 2020 08:06:14 +1000 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> References: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> Message-ID: I don't think that it's unreasonable in general to expect people to explore a codebase via IDE tooling. But given Haskell's current situation on that front, I currently agree with your approach to Haskell imports/exports. Ignat, I agree with you that explicit imports/exports involve unnecessary typing. I call this "busywork". Explicit exports still seem valuable for encapsulation, avoiding name clashes, and in the case of GHC they unlock a bit more optimisation. In this case I think that we should automate that busywork, and hopefully the recent Haskell IDE work gives us a path in that direction. On Fri, 18 Sep 2020, 3:54 am Olaf Klinke, wrote: > Dear Ignat, > > have you seen > https://wiki.haskell.org/Import_modules_properly > https://wiki.haskell.org/Qualified_names > > I find the arguments convincing. Even in my own packages I sometimes > get lost where a certain function was imported from. When neither > exports nor imports are done explicitly, you usually have only two > choices: > 1. search all sources (e.g. with grep -l) > 2. rely on the haddock index > Maybe your IDE can do that for you, but you can't expect all downstream > users or all your colleagues to do the same. > > -- Olaf > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From sumitraja at gmail.com Fri Sep 18 01:53:32 2020 From: sumitraja at gmail.com (Sumit Raja) Date: Fri, 18 Sep 2020 11:53:32 +1000 Subject: [Haskell-cafe] ghcxxxx.exe in Local\Temp in Windows Message-ID: Hi, Running ghc 8.8.4 + cabal 3.2 on Windows. M365 malware scanner identified that ghcbdbc.exe in my temp directory was the Wacatac malware. I checked and there is a large number of these ghcxxxx.exe files in there. They look like build artefacts from cabal repl. The malware scanner process tree is cabal -> gcc -> realgcc -> collect2 with ld.exe being the exec that created it. Admin is confident that Wacatac was removed or it was a false alarm but I need to provide some details on why this build artefact is created. Anyone know what these files are? Thanks Sumit -------------- next part -------------- An HTML attachment was scrubbed... URL: From qdunkan at gmail.com Fri Sep 18 01:58:05 2020 From: qdunkan at gmail.com (Evan Laforge) Date: Thu, 17 Sep 2020 18:58:05 -0700 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> Message-ID: On Thu, Sep 17, 2020 at 3:08 PM Isaac Elliott wrote: > > I don't think that it's unreasonable in general to expect people to explore a codebase via IDE tooling. But given Haskell's current situation on that front, I currently agree with your approach to Haskell imports/exports. > > Ignat, I agree with you that explicit imports/exports involve unnecessary typing. I call this "busywork". Explicit exports still seem valuable for encapsulation, avoiding name clashes, and in the case of GHC they unlock a bit more optimisation. > > In this case I think that we should automate that busywork, and hopefully the recent Haskell IDE work gives us a path in that direction. I mention this every time it comes up, but you can automate it right now, and I've been doing it for the last 10 years or so, no IDE needed. The tool I wrote is called fix-imports, but there are a number of others floating around. So I don't agree that writing imports is busywork, you never had to write that stuff in the first place, if you really didn't want to. Another benefit of qualifications for navigation is that they can disambiguate tags. Fancier IDE-like tools could do that without the qualification, but tags are here now and I think they actually work better. Actually on further thought, the same thing that disambiguates based on qualification could also easily disambiguate without it, so maybe this is not a real benefit after all. I just happened to set up the former and not the latter :) My first step looking at any third-party code is to tags the whole lot but for whatever reason I still much prefer qualifications. Few people use them though. For explicit exports, I often leave them off for convenience during development, but put them in when it settles down. I don't think it unlocks any optimization in code generation, but it does make rebuilds faster because it won't change the hi file if you changed a non-exported non-inlined function. You also get unused warnings. When I add the export list, I often append '#ifdef TEST , module This.Module #endif' so that tests still have total visibility. I prefer this to the Internal module approach because I don't like zillions of modules with the same name, and I don't want to have to structure code to the whims of tests, and I like to get unused symbol warnings from ghc without having to go to weeder. One benefit to explicit exports that surprises me is the trivial detection of unused functions. On several occasions I have done extra work or even just extra thinking to try to preserve a caller, only to find out that due to the changes I just made, it has no other users and I could have just deleted it without thinking hard. Yes, a simple grep would have revealed that, but I will instantly notice ghc saying unused symbol and I might not think to insert manual greps into my planning process. Often it's a whole chain of callers that can be deleted, and ghc will find those all immediately. From kindaro at gmail.com Fri Sep 18 07:22:45 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Fri, 18 Sep 2020 12:22:45 +0500 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: Message-ID: Thank you Henning. This is true. When one wishes to provide an abstract type, surely one would like to re-export the type constructor, but not the associated data constructors — so explicit exports become unavoidable. I have not accounted for this detail. > >> You need explicit exports in the public interface, in my example module > >> "A". > > > > How so? > > My example should be like so: > > module A.Internal where > > data T = Private Integer > > > module A ( > T, -- do not export constructor > f, > ) where > > import A.Internal (T(Private)) > > f :: FooBar > f = do something with Private > > > You would need the export list in A for exposing T, but not Private. From tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk Fri Sep 18 07:39:50 2020 From: tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk (Tom Ellis) Date: Fri, 18 Sep 2020 08:39:50 +0100 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> Message-ID: <20200918073950.GA31772@cloudinit-builder> On Fri, Sep 18, 2020 at 08:06:14AM +1000, Isaac Elliott wrote: > I don't think that it's unreasonable in general to expect people to explore > a codebase via IDE tooling. Implicit imports prevent people easily understanding code that is presented on GitHub, for example. I think this is the main reason I dislike implicit imports, my own inconvenience coming a close second. From simonpj at microsoft.com Fri Sep 18 07:48:11 2020 From: simonpj at microsoft.com (Simon Peyton Jones) Date: Fri, 18 Sep 2020 07:48:11 +0000 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> Message-ID: | For explicit exports, I often leave them off for convenience during | development, but put them in when it settles down. I don't think it | unlocks any optimization in code generation Actually, it does make a difference to optimisation. If a function is known not to be exported, then GHC knows every one of its call sites. Eg so * It may be called only once, and can be inlined (regardless of size) at that call site. * If we get a worker/wrapper split, we'll inline the wrapper at all the call sites. If it's not exported, GHC can discard the wrapper. * CalledArity analysis can be much more aggressive when it can see all call sites. I don't know anyone who has measured the perf or binary-size benefits of limiting export lists. It's probably not huge. But it's not zero. Simon | -----Original Message----- | From: Haskell-Cafe On Behalf Of Evan | Laforge | Sent: 18 September 2020 02:58 | To: Isaac Elliott | Cc: Olaf Klinke ; Haskell Cafe | Subject: Re: [Haskell-cafe] Are explicit exports and local imports desirable | in a production application? | | On Thu, Sep 17, 2020 at 3:08 PM Isaac Elliott wrote: | > | > I don't think that it's unreasonable in general to expect people to | explore a codebase via IDE tooling. But given Haskell's current situation on | that front, I currently agree with your approach to Haskell imports/exports. | > | > Ignat, I agree with you that explicit imports/exports involve unnecessary | typing. I call this "busywork". Explicit exports still seem valuable for | encapsulation, avoiding name clashes, and in the case of GHC they unlock a | bit more optimisation. | > | > In this case I think that we should automate that busywork, and hopefully | the recent Haskell IDE work gives us a path in that direction. | | I mention this every time it comes up, but you can automate it right | now, and I've been doing it for the last 10 years or so, no IDE | needed. The tool I wrote is called fix-imports, but there are a | number of others floating around. So I don't agree that writing | imports is busywork, you never had to write that stuff in the first | place, if you really didn't want to. | | Another benefit of qualifications for navigation is that they can | disambiguate tags. Fancier IDE-like tools could do that without the | qualification, but tags are here now and I think they actually work | better. Actually on further thought, the same thing that | disambiguates based on qualification could also easily disambiguate | without it, so maybe this is not a real benefit after all. I just | happened to set up the former and not the latter :) My first step | looking at any third-party code is to tags the whole lot but for | whatever reason I still much prefer qualifications. Few people use | them though. | | For explicit exports, I often leave them off for convenience during | development, but put them in when it settles down. I don't think it | unlocks any optimization in code generation, but it does make rebuilds | faster because it won't change the hi file if you changed a | non-exported non-inlined function. You also get unused warnings. | When I add the export list, I often append '#ifdef TEST , module | This.Module #endif' so that tests still have total visibility. I | prefer this to the Internal module approach because I don't like | zillions of modules with the same name, and I don't want to have to | structure code to the whims of tests, and I like to get unused symbol | warnings from ghc without having to go to weeder. | | One benefit to explicit exports that surprises me is the trivial | detection of unused functions. On several occasions I have done extra | work or even just extra thinking to try to preserve a caller, only to | find out that due to the changes I just made, it has no other users | and I could have just deleted it without thinking hard. Yes, a simple | grep would have revealed that, but I will instantly notice ghc saying | unused symbol and I might not think to insert manual greps into my | planning process. Often it's a whole chain of callers that can be | deleted, and ghc will find those all immediately. | _______________________________________________ | Haskell-Cafe mailing list | To (un)subscribe, modify options or view archives go to: | https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fmail.haskel | l.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fhaskell- | cafe&data=02%7C01%7Csimonpj%40microsoft.com%7C722f388e97ba432419d308d85b | 7673dd%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637359911600724047&s | data=M4h%2F%2FjCg7iLdpoIYrgcThIMAtCRNdlYSdbPW%2BtDXKV0%3D&reserved=0 | Only members subscribed via the mailman list are allowed to post. From kindaro at gmail.com Fri Sep 18 08:38:13 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Fri, 18 Sep 2020 13:38:13 +0500 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> Message-ID: I would like to summarize the conversation so far. Surely I am not free from bias, so please object if my representation of some of the opinions I am re-stating is unfair or incomplete to their disadvantage. I shall present arguments and counter-arguments in the shape of a tree, with markup and indentation. ### Many people said that they like qualified imports. Note that my opening message did not speak against that. I suppose I should have stated openly that I am all for appropriate use of qualified imports with descriptive names, such as `Text.pack`, `Set.fromList` and even `List.sort` — although `Maybe.fromMaybe` may be be too much. I think there is wide agreement about this. ## Imports. ### Reasons why explicit imports may be preferable: * Resolution of names to the originating modules. - Note that this still leaves it to the reader to resolve modules to packages. That is, unless one also insists on using package imports, which I see very rarely. - Modern tools can resolve names as if by magic, qualified or not. Surely we can expect more progress in this direction. * Can we expect online, multilingual code repositories to do this for us? ### Cases when explicit imports are required: None so far. _(Disambiguation can be done with qualified imports.)_ ## Exports. ### Reasons why explicit exports may be preferable: * There may be a helper function that is used by several exported functions, so it cannot be put in a `where` clause — if it is absent from the explicit export list, GHC will be able to optimize better. * Haddock magic: changing the order of exports, adding headers and sections. - Most if not all of these effects can be accomplished without export lists. * Detection of dead code by GHC. - Tools exist to detect dead code without the use of explicit exports. ### Cases when explicit exports are required: * Exporting the type constructor of an abstract type, but not its data constructors. * Re-exporting modules. From luc.duponcheel at gmail.com Fri Sep 18 14:32:14 2020 From: luc.duponcheel at gmail.com (Luc Duponcheel) Date: Fri, 18 Sep 2020 16:32:14 +0200 Subject: [Haskell-cafe] program description based programming Message-ID: Hellot Just started with the new version of the PDBP library. https://github.com/PDBP Work in progress. Expect frequent additions (and changes). Luc -- __~O -\ <, (*)/ (*) reality goes far beyond imagination -------------- next part -------------- An HTML attachment was scrubbed... URL: From misja.alma at gmail.com Sat Sep 19 15:02:41 2020 From: misja.alma at gmail.com (Misja Alma) Date: Sat, 19 Sep 2020 17:02:41 +0200 Subject: [Haskell-cafe] Readable Haskell Message-ID: Hi, I have been writing Haskell in my spare time for a couple of years now, but when I showed some code lately to a friend he remarked that he didn't find it very readable. Actually I agree, when I look at my own code of a couple of months old I have trouble figuring out too what exactly it is doing. I'm coming from a Java and Scala background and there, especially for Java, are some generally accepted best practices that make sure that your teammates don't have too much trouble reading your code. E.g. write short functions with a single responsibility, use variable, class and function names that explain what they are meant for, etc. I think some of those best practices, like short functions with single responsibility, are useful for Haskell as well. But Haskell is a different language than Java and has its own strong points and pitfalls regarding readability, so it probably needs different coding standards as well. I have been looking on the Internet if I could find some tips about improving readability but all I could find was http://www.haskellforall.com/. Although there are some useful tips in there, this site seems to be aimed at making Haskell easier to read for newcomers from other languages. What I am interested in are tips from real projects that are built by real teams. Does anybody have any tips, or are there some sites or books that I could read about this topic? Thanks, Misja -------------- next part -------------- An HTML attachment was scrubbed... URL: From branimir.maksimovic at gmail.com Sat Sep 19 15:06:17 2020 From: branimir.maksimovic at gmail.com (Branimir Maksimovic) Date: Sat, 19 Sep 2020 17:06:17 +0200 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: Generally write functions with meaningfull names. Using symbols should be understandable and  clear. Generally Haskell is easy to read. Greets, Branimir. On 9/19/20 5:02 PM, Misja Alma wrote: > Hi, > > I have been writing Haskell in my spare time for a couple of years > now, but when I showed some code lately to a friend he remarked that > he didn't find it very readable. Actually I agree, when I look at my > own code of a couple of months old I have trouble figuring out too > what exactly it is doing. > > I'm coming from a Java and Scala background and there, especially for > Java, are some generally accepted best practices that make sure that > your teammates don't have too much trouble reading your code. E.g. > write short functions with a single responsibility, use variable, > class and function names that explain what they are meant for, etc. > > I think some of those best practices, like short functions with single > responsibility, are useful for Haskell as well. But Haskell is a > different language than Java and has its own strong points and > pitfalls regarding readability, so it probably needs different coding > standards as well. > > I have been looking on the Internet if I could find some tips about > improving readability but all I could find was > http://www.haskellforall.com/. Although there are some useful tips in > there, this site seems to be aimed at making Haskell easier to read > for newcomers from other languages. What I am interested in are tips > from real projects that are built by real teams. > Does anybody have any tips, or are there some sites or books that I > could read about this topic? > > Thanks, > Misja > > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lemming at henning-thielemann.de Sat Sep 19 15:10:40 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Sat, 19 Sep 2020 17:10:40 +0200 (CEST) Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: On Sat, 19 Sep 2020, Misja Alma wrote: > Does anybody have any tips, or are there some sites or books that I > could read about this topic? It may be a bit old but we have some articles on style in the Haskell Wiki: https://wiki.haskell.org/Category:Style https://wiki.haskell.org/Category:Idioms From tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk Sat Sep 19 15:20:01 2020 From: tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk (Tom Ellis) Date: Sat, 19 Sep 2020 16:20:01 +0100 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: <20200919152001.GC31772@cloudinit-builder> On Sat, Sep 19, 2020 at 05:02:41PM +0200, Misja Alma wrote: > I have been writing Haskell in my spare time for a couple of years now, but > when I showed some code lately to a friend he remarked that he didn't find > it very readable. Actually I agree, when I look at my own code of a couple > of months old I have trouble figuring out too what exactly it is doing. Could you perhaps show the code? Then I may be able to give suggestions about how to improve it. I have written a few articles about refactoring to improve readability it Haskell, for example: * http://h2.jaguarpaw.co.uk/posts/good-design-and-type-safety-in-yahtzee/ * http://h2.jaguarpaw.co.uk/posts/refactoring-neural-network/ Tom From misja.alma at gmail.com Sat Sep 19 15:32:00 2020 From: misja.alma at gmail.com (Misja Alma) Date: Sat, 19 Sep 2020 17:32:00 +0200 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: Thanks, these links are really useful! This definitely answers part of my question. But what would still be really useful are some more or less generally accepted best practices about variable naming, indentation, when to use nested functions vs when to prefer keeping functions short, etc with regards to readability. On Sat, 19 Sep 2020 at 17:10, Henning Thielemann < lemming at henning-thielemann.de> wrote: > > On Sat, 19 Sep 2020, Misja Alma wrote: > > > Does anybody have any tips, or are there some sites or books that I > > could read about this topic? > > It may be a bit old but we have some articles on style in the Haskell > Wiki: > https://wiki.haskell.org/Category:Style > https://wiki.haskell.org/Category:Idioms > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ollie at ocharles.org.uk Sat Sep 19 16:22:33 2020 From: ollie at ocharles.org.uk (Oliver Charles) Date: Sat, 19 Sep 2020 17:22:33 +0100 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: For indentation and very mechanical formatting, I suggest just using something like hindent, brittany or ormolu (all available at your local Hackage). For naming, I follow Chris Done's suggestion: https://chrisdone.com/posts/german-naming-convention/ On Sat, 19 Sep 2020, at 4:32 PM, Misja Alma wrote: > Thanks, these links are really useful! This definitely answers part of my question. > > But what would still be really useful are some more or less generally accepted best practices about variable naming, indentation, when to use nested functions vs when to prefer keeping functions short, etc with regards to readability. > > On Sat, 19 Sep 2020 at 17:10, Henning Thielemann wrote: >> >> On Sat, 19 Sep 2020, Misja Alma wrote: >> >> > Does anybody have any tips, or are there some sites or books that I >> > could read about this topic? >> >> It may be a bit old but we have some articles on style in the Haskell >> Wiki: >> https://wiki.haskell.org/Category:Style >> https://wiki.haskell.org/Category:Idioms > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brodyberg at gmail.com Sat Sep 19 16:28:04 2020 From: brodyberg at gmail.com (Brody Berg) Date: Sat, 19 Sep 2020 09:28:04 -0700 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: As a person who has been trying to learn Haskell for years I have a private joke related to reading Haskell source which is “It has been x days since I’ve seen completely unintelligible Haskell.” My sense is that language extensions are a big part of this - they can have a significant impact on readability and for a novice it is not at all clear how to differentiate the text changes wrought by the extension. And in the presence of multiple or even many extensions? Forget it. So yes, I agree Haskell can be hard to read - and we haven’t even dredged up the point/point-free debate! With Kindness, Brody On Sat, Sep 19, 2020 at 08:33 Misja Alma wrote: > Thanks, these links are really useful! This definitely answers part of my > question. > > But what would still be really useful are some more or less generally > accepted best practices about variable naming, indentation, when to use > nested functions vs when to prefer keeping functions short, etc with > regards to readability. > > On Sat, 19 Sep 2020 at 17:10, Henning Thielemann < > lemming at henning-thielemann.de> wrote: > >> >> >> >> On Sat, 19 Sep 2020, Misja Alma wrote: >> >> >> >> >> >> > Does anybody have any tips, or are there some sites or books that I >> >> >> > could read about this topic? >> >> >> >> >> >> It may be a bit old but we have some articles on style in the Haskell >> >> >> Wiki: >> >> >> https://wiki.haskell.org/Category:Style >> >> >> https://wiki.haskell.org/Category:Idioms >> >> >> > > _______________________________________________ > > Haskell-Cafe mailing list > > To (un)subscribe, modify options or view archives go to: > > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lemming at henning-thielemann.de Sat Sep 19 16:33:09 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Sat, 19 Sep 2020 18:33:09 +0200 (CEST) Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: On Sat, 19 Sep 2020, Oliver Charles wrote: > For naming, I follow Chris Done's > suggestion: https://chrisdone.com/posts/german-naming-convention/ Interesting perspective on German language. :-) Btw. I would prefer ColumnExpression.update. Or even ColExp.update because at the top of the module I would have import qualified ColumnExpression as ColExp From vamchale at gmail.com Sat Sep 19 17:03:28 2020 From: vamchale at gmail.com (Vanessa McHale) Date: Sat, 19 Sep 2020 12:03:28 -0500 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: My 2¢: my style comes from writing silly code and revisiting it and realizing what to do differently. I use stylish-haskell to order my import and I use hlint on my code. I think compared to other languages, one writes more small functions. And I rely more on haddock + type signatures - I've learned some ways to avoid writing bad haddocks by getting frustrated by packages on Hackage. Cheers, Vanessa McHale On 9/19/20 10:02 AM, Misja Alma wrote: > Hi, > > I have been writing Haskell in my spare time for a couple of years > now, but when I showed some code lately to a friend he remarked that > he didn't find it very readable. Actually I agree, when I look at my > own code of a couple of months old I have trouble figuring out too > what exactly it is doing. > > I'm coming from a Java and Scala background and there, especially for > Java, are some generally accepted best practices that make sure that > your teammates don't have too much trouble reading your code. E.g. > write short functions with a single responsibility, use variable, > class and function names that explain what they are meant for, etc. > > I think some of those best practices, like short functions with single > responsibility, are useful for Haskell as well. But Haskell is a > different language than Java and has its own strong points and > pitfalls regarding readability, so it probably needs different coding > standards as well. > > I have been looking on the Internet if I could find some tips about > improving readability but all I could find > was http://www.haskellforall.com/. Although there are some useful tips > in there, this site seems to be aimed at making Haskell easier to read > for newcomers from other languages. What I am interested in are tips > from real projects that are built by real teams. > Does anybody have any tips, or are there some sites or books that I > could read about this topic? > > Thanks, > Misja > > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 659 bytes Desc: OpenPGP digital signature URL: From misja.alma at gmail.com Sat Sep 19 17:07:38 2020 From: misja.alma at gmail.com (Misja Alma) Date: Sat, 19 Sep 2020 19:07:38 +0200 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: >> For naming, I follow Chris Done's suggestion: https://chrisdone.com/posts/german-naming-convention/ Thanks! I didn't know it had this funny name, but the German name convention is also widely used in the Java world. But I wonder how well it works in Haskell, because unlike Java, in Haskell a lot of stuff can happen in a single line. With those long variable names, you'd have to break those lines several times. I haven't tried it yet, but I wonder how readable that is in Haskell? Or am I looking at the wrong problem here, and should I first of all try to have as little happening in a single line of Haskell as possible? On Sat, 19 Sep 2020 at 18:22, Oliver Charles wrote: > For indentation and very mechanical formatting, I suggest just using > something like hindent, brittany or ormolu (all available at your local > Hackage). > > For naming, I follow Chris Done's suggestion: > https://chrisdone.com/posts/german-naming-convention/ > > On Sat, 19 Sep 2020, at 4:32 PM, Misja Alma wrote: > > Thanks, these links are really useful! This definitely answers part of my > question. > > But what would still be really useful are some more or less generally > accepted best practices about variable naming, indentation, when to use > nested functions vs when to prefer keeping functions short, etc with > regards to readability. > > On Sat, 19 Sep 2020 at 17:10, Henning Thielemann < > lemming at henning-thielemann.de> wrote: > > > On Sat, 19 Sep 2020, Misja Alma wrote: > > > Does anybody have any tips, or are there some sites or books that I > > could read about this topic? > > It may be a bit old but we have some articles on style in the Haskell > Wiki: > https://wiki.haskell.org/Category:Style > https://wiki.haskell.org/Category:Idioms > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ietf-dane at dukhovni.org Sat Sep 19 20:57:27 2020 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Sat, 19 Sep 2020 16:57:27 -0400 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: <20200919205727.GJ64864@straasha.imrryr.org> On Sat, Sep 19, 2020 at 07:07:38PM +0200, Misja Alma wrote: > But I wonder how well it works in Haskell, because unlike Java, in Haskell > a lot of stuff can happen in a single line. One idiom for making "a lot of stuff in a single line" easier to read is seen in: https://hackage.haskell.org/package/streaming-attoparsec-1.0.0.1/docs/Data-Attoparsec-ByteString-Streaming.html ... import Data.Function ((&)) main :: IO () main = Q.getContents -- raw bytes & AS.parsed lineParser -- stream of parsed `Maybe Int`s; blank lines are `Nothing` & void -- drop any unparsed nonsense at the end -- [1] & S.split Nothing -- split on blank lines & S.maps S.concat -- keep `Just x` values in the sub-streams (cp. catMaybes) & S.mapped S.sum -- sum each substream & S.print -- stream results to stdout lineParser = Just <$> A.scientific <* A.endOfLine <|> Nothing <$ A.endOfLine Here, the function composition flows from left to right, in fact top to bottom, rather than right to left (bottom to top over multiple lines). The key ingredient is the (&) operator which puts the argument on the left and the function on the right. I've also seen (the first borrowed from F#): (|>): a -> (a -> b) -> b (|.>): (a -> b) -> (b -> c) -> c (|$>) f a -> (a -> b) -> f b (|*>) f a -> f (a -> b) -> f b And of course Conduit's (.|) is another instance of left-to-right style for expressing long composition chains. Returning to the streaming example, since in `streaming` transformations of streams are performed via function application (no new operator like Conduit's (.|)), the left-to-right style uses (&). Of course even with the flow made clear, and names well chosen, one still has to come to grips with some rather powerful, highly polymorphic idioms, whose purpose in each context may warrant a comment in code that is to be accessible to those still learning the ropes. The somewhat non-obvious "void" here is but a mild example. -- Viktor. [1] One slightly non-obvious thing at first blush about streams is that "void" does not perturb the content of the stream, it only drops the stream's terminal value. Streams are functors in that terminal value, so it turns out, surprisingly at first, that the two variants of "print the stream" below are identical. s :: Show a => Stream (Of a) IO r s = ... -- The relevant functor here is: F r = (Stream (Of a) IO) r -- thus, void s :: Stream (Of a) IO () S.print . void $ s -- same as void . S.print $ s So, for a reader not steeped in the streaming library, one might even comment on the role of "void" in more detail: ... & AS.parsed lineParser -- stream of parsed `Maybe Int`s; blank lines are `Nothing` & void -- Replace the stream `Return` value `r` with `()` -- discarding parser errors, see 'AS.parsed'. ... So that nobody is left wondering at stream processing continuing past "void", which in more mundate contexts one expects to not return anything useful to be further processed. This briefly caught me by surprise in the "all in one line" right-to-left example at the end of the document: S.print . void $ AS.parsed (A.scientific <* A.many' A.space) "12.3 4.56 78.9" From lemmih at gmail.com Sun Sep 20 06:10:17 2020 From: lemmih at gmail.com (Lemmih) Date: Sun, 20 Sep 2020 14:10:17 +0800 Subject: [Haskell-cafe] [ANN] Reanimate v1.0 Message-ID: It is my great pleasure to announce version 1.0 of the Reanimate animation library. Reanimate aims to be a batteries-included way of creating animations and illustrations. To see it in action, visit: https://reanimate.github.io/ Reanimate wouldn't be here without help and I'd like to thank Jan Hrček for writing the Elm viewer, Shaurya Gupta for performance improvements, William Yaoh for writing documentation, Mike Pilgrem for ImageMagick v7 support, and Xie Ruifeng for LaTeX and text encoding fixes. Furthermore, I'd like to thank Eve, Joe, Rodrigo, Brad, Aron, and Ben for their help and support. Finally, I'd like to thank the GitHub sponsors who are financing server costs and making Reanimate the best it can be. Hackage: https://hackage.haskell.org/package/reanimate Haddock: https://hackage.haskell.org/package/reanimate/docs/Reanimate.html Tutorial: https://reanimate.readthedocs.io/en/latest/tut_equation/ GitHub: https://github.com/reanimate/reanimate/ Website: https://reanimate.github.io/ Discord: https://discord.gg/Qs28Dv6 Sponsors: https://github.com/sponsors/Lemmih Best wishes, David. From ben.franksen at online.de Sun Sep 20 10:34:13 2020 From: ben.franksen at online.de (Ben Franksen) Date: Sun, 20 Sep 2020 12:34:13 +0200 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: Am 19.09.20 um 18:22 schrieb Oliver Charles: > For naming, I follow Chris Done's suggestion: > https://chrisdone.com/posts/german-naming-convention/ FWIW, I tend to disagree with the position presented in this blog, at least in the generality in which it is stated. Yes, long names can be useful to enhance readability, but this is mostly the case when naming highly specific things. In Haskell, core algorithms often implement some algebraic theory. It is not by accident that mathematical formulae, including applications in the natural sciences, almost exclusively use single letters or very short names. Understanding such formulae at a glance requires familiarity with the notational conventions used, but once you have achieved that familiarity, the short names actually /improve/ readability, assuming the naming convention is applied in a consistent manner. It is also no accident that formulae typically make up only a part of the complete text. A complex formula needs to be accompanied by precise definitions and explanations. Whenever you write code for which it is not immediately obvious why it was done in exactly this way, add a comment that explains what is going on! To illustrate my point, here is some code that implements a highly non-trivial algebra I wrote some time ago: https://hub.darcs.net/darcs/darcs-screened/browse/src/Darcs/Patch/V3/Core.hs#176 I think using longer names in this code would be awkward and distracting. Also note that the naming convention is documented: https://hub.darcs.net/darcs/darcs-screened/browse/src/Darcs/Patch/V3/Core.hs#131 I even introduced operator symbols +| and -| as synonyms for some Data.Set operations in order to make the formulae more concise (and thus, IMHO, easier to read). It goes without saying that you cannot read non-trivial code like this as you read english prose. There is simply no way to understand it without having at least a rough idea of the underlying theoretical foundations. Cheers Ben From kindaro at gmail.com Sun Sep 20 12:02:51 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Sun, 20 Sep 2020 17:02:51 +0500 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: Ben, I think your proposition is righteous and uncontestable in theory, and I stand by you. Unfortunately the practice hardly aligns with the sermon. No one writes Mathematics in monospace ASCII left to right. They go to great lengths to actually type set things, generously use large and small font, bold and cursive, write above and below the line, use Greek and Hebrew and a myriad infix symbols. And for a reason — compare a latex source of a _«notation heavy»_ paper with it rendered. One may approach mathematical style in program source with generous use of Unicode, but few dare. Even type setting code in proportional font is considered heresy by many — just so that they may banish proper tabulation and _indent with spaces_. So, even the proponents of the mathematical style do not care to follow it as soon as it requires a little effort. _(Hoω h∀rd may it be to g∃t some ∪nic⊕de on one's kεyb∅arδ? See also the packages `base-unicode-symbols`[1] and `containers-unicode-symbols`[2].)_ Therefore I think for many it is merely an excuse for writing ugly code. The Darcs code you show illustrates the point Chris Done speaks for as well. Observe top level names: `displayPatch`, `commuteConflicting`, `cleanMerge` — quite German! Then there is `ctxAddInvFL` and `mapFL_FL`, but that from other modules. Finally, I tried to find out what `Prim` stands for — I went as far as to the index of `darcs` on Hackage[3] but no luck. And `prim` is the most frequent in the list of words of the module, with 125 occurrences in normalized case. Primitive? Primary? Prime? Primavera? [1]: https://hackage.haskell.org/package/base-unicode-symbols [2]: https://hackage.haskell.org/package/containers-unicode-symbols [3]: https://hackage.haskell.org/package/darcs-2.16.2/docs/doc-index-P.html From migmit at gmail.com Sun Sep 20 12:18:27 2020 From: migmit at gmail.com (MigMit) Date: Sun, 20 Sep 2020 14:18:27 +0200 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> True, but it might have something to do with the fact that mathematics was invented long before monospaced fonts. And hand-writing integrals, or quantifiers, or the empty-set symbol, is just as easy as writing digits. I would rather agree with Ben: there are lots of places where long identifiers are distracting. Sometimes they make sense, but a lot of times they just don't. > On 20 Sep 2020, at 14:02, Ignat Insarov wrote: > > Ben, I think your proposition is righteous and uncontestable in > theory, and I stand by you. Unfortunately the practice hardly aligns > with the sermon. > > No one writes Mathematics in monospace ASCII left to right. They go to > great lengths to actually type set things, generously use large and > small font, bold and cursive, write above and below the line, use > Greek and Hebrew and a myriad infix symbols. And for a reason — > compare a latex source of a _«notation heavy»_ paper with it rendered. > > One may approach mathematical style in program source with generous > use of Unicode, but few dare. Even type setting code in proportional > font is considered heresy by many — just so that they may banish > proper tabulation and _indent with spaces_. So, even the proponents of > the mathematical style do not care to follow it as soon as it requires > a little effort. _(Hoω h∀rd may it be to g∃t some ∪nic⊕de on one's > kεyb∅arδ? See also the packages `base-unicode-symbols`[1] and > `containers-unicode-symbols`[2].)_ Therefore I think for many it is > merely an excuse for writing ugly code. > > The Darcs code you show illustrates the point Chris Done speaks for as > well. Observe top level names: `displayPatch`, `commuteConflicting`, > `cleanMerge` — quite German! Then there is `ctxAddInvFL` and > `mapFL_FL`, but that from other modules. Finally, I tried to find out > what `Prim` stands for — I went as far as to the index of `darcs` on > Hackage[3] but no luck. And `prim` is the most frequent in the list of > words of the module, with 125 occurrences in normalized case. > Primitive? Primary? Prime? Primavera? > > [1]: https://hackage.haskell.org/package/base-unicode-symbols > [2]: https://hackage.haskell.org/package/containers-unicode-symbols > [3]: https://hackage.haskell.org/package/darcs-2.16.2/docs/doc-index-P.html > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. From svenpanne at gmail.com Sun Sep 20 15:06:38 2020 From: svenpanne at gmail.com (Sven Panne) Date: Sun, 20 Sep 2020 17:06:38 +0200 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> Message-ID: Am So., 20. Sept. 2020 um 14:20 Uhr schrieb MigMit : > [...] there are lots of places where long identifiers are distracting. > Sometimes they make sense, but a lot of times they just don't. > I think a good rule of thumb is: The length of an identifier should be proportional to the size of the scope in which it is valid. As an example: If you design an API, names like "openBinaryFile", "handleValidationError", "primaryDrawingContext" are a good idea. If you have a simple 1-line or 2-line helper function, one-letter names can make things vastly more readable, because you can see the "meat" of the code more easily without drowning in 20-letter identifiers spilled over 5 lines. So just using "ctx" or even "c" for "primaryDrawingContext" in a one-liner can improve things. This is even more true when you have a type annotation with long, descriptive type names for this function. But all of this is very subjective, and in the end writing good, readable code is a bit of an art. There are guidelines and tips on how to do this, but there can never be hard and fast rules which will make everybody happy. -------------- next part -------------- An HTML attachment was scrubbed... URL: From taeer at necsi.edu Sun Sep 20 16:33:11 2020 From: taeer at necsi.edu (Taeer Bar-Yam) Date: Sun, 20 Sep 2020 09:33:11 -0700 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: < References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> Message-ID: <1600619568.wvzvea6eke.astroid@rebel.none> Can we agree that it's possible to go too far? Even in the global scope. https://github.com/Quotation/LongestCocoa. I think another factor that comes into play is how frequently you use the function/value. If it comes up only once in a while, then every time someone encounters it they are likely to have to remind themselves what all the abreviations mean. If it comes up more frequently, then they likely already saw it 10 lines ago, and remember. If it comes up very frequently, then whatever name you assign it takes on its own meaning. Think about `map` or, in bash land `ls` and `cd`. Unless you just started using bash, you're not going to think "Oh. cd stands for 'change directory'". It's just… cd. And at the same time, using a long name for something that's going to come up every couple of lines is, IMO, distracting. Having all of the information be within what your eye can easily take in at once has huge advantages. Not to mention, can you imagine writing `changeDirectory` and `printDirectoryContents` every time? It's a balancing act. Obviously, the opposite extreme carries its own problems. --Taeer From monkleyon at gmail.com Sun Sep 20 18:37:28 2020 From: monkleyon at gmail.com (MarLinn) Date: Sun, 20 Sep 2020 20:37:28 +0200 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: <20200919205727.GJ64864@straasha.imrryr.org> References: <20200919205727.GJ64864@straasha.imrryr.org> Message-ID: > main :: IO () > main = Q.getContents -- raw bytes > & AS.parsed lineParser -- stream of parsed `Maybe Int`s; blank lines are `Nothing` > & void -- drop any unparsed nonsense at the end -- [1] > & S.split Nothing -- split on blank lines > & S.maps S.concat -- keep `Just x` values in the sub-streams (cp. catMaybes) > & S.mapped S.sum -- sum each substream > & S.print -- stream results to stdout There's still quite a bit that can be improved here. First of all: comments are good. But whenever you write a comment, ask yourself "could I choose a better name instead?" Make the code self-documenting at all usage sites by choosing a good name once. It's a good idea for every language, but this piece of code is a good example of how to apply it. So, first step: main ∷ IO () main = Q.getContents & parseLines & dropUnparsed -- Add the explanation/link definition side & splitOnBlankLines & catMaybes' -- why write "it's catMaybes", when you could just write "catMaybes"? & S.mapped S.sum & S.print Do you need more functions this way to store the names? Yes. Which is a good thing, because they might be reusable. Of course there's a limit; if every function fits in half a line, you've probably gone too far. Second step: Thinking from left to right is a remainder from thinking imperatively. If you just turn around the top level of the code, the reader is forced to a game of ping pong while reading, so it can even make it harder to understand. So let's get rid of that (&) crowbar. main ∷ IO ()main = S.print     . S.mapped S.sum     . catMaybes' -- by the way, there's probablya better name for what it's actually doing. "dropBlankLines", maybe?      . splitOnBlankLines      . dropUnparsed     . parseLines     $ Q.getContents There's more reasons why going against Haskell's natural grain is a bad idea, and you provided the perfect hook to talk about it: > (|>): a -> (a -> b) -> b > (|.>): (a -> b) -> (b -> c) -> c > (|$>) f a -> (a -> b) -> f b > (|*>) f a -> f (a -> b) -> f b Operators have the inherent problem that there aren't many symbols to choose from. That means that almost all operators are overloaded. For example, (|>) will be confused with the one from Data.Sequence. Yes, there's unicode. I love unicode (see that sneaky little "∷" up there?) so I've tried using Unicode in operators before, but one single person using the project had their device set to a C locale and so my whole library was search-and-replaced. It's still 1968 out there, so there's like 10 symbols to choose from. And even if you could use more symbols, operators still have the inherent problem that they can't contain any letters. What exactly does (>>?$>) mean? Or (|>||>)? Or (<<*>@)? So why not rely on the operators that are widely used instead of crowbaring new ones in just so that we can keep programming in C. -------------- next part -------------- An HTML attachment was scrubbed... URL: From kindaro at gmail.com Sun Sep 20 19:30:23 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Mon, 21 Sep 2020 00:30:23 +0500 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: <1600619568.wvzvea6eke.astroid@rebel.none> References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> <1600619568.wvzvea6eke.astroid@rebel.none> Message-ID: Command shell is a very old and peculiar human interface — if human at all. They had 80 character wide screens and visibly lagging connexion. We have retina screens, GPU accelerated rendering and magical auto-completion. Imagine those commands were buttons that you could press. Would you prefer a button to say _«`ls`»_ or _«list files»_, _«`cd`»_ or _«change directory»_? For a vivid example, imagine a web site where you have `lgn` and `plrq` instead of _«log in»_ and _«pull request»_. Would you like that? Getting back to Mathematics — this is where abstraction and notation come in. We can give names to things and we can use scoping. But neither mathematicians nor system administrators invent new terminology for every next paper or script — maybe a few key words. Industrial programming is yet another thing. I imagine when you have a record with a hundred fields it pays off to have longish field labels. And you cannot expect the next person to read your code from top to bottom so that they get used to its peculiar vocabulary. For example, today I am merging two branches of a 10 thousand lines code base that diverged last Spring. Guessing the difference between `rslt` and `res` is the last thing I need. You do not need to call your context _«`ctx`»_ and your result _«`rslt`»_ — there are already words for it. There was a time when every character needed to be typed and there was a rectangle of only some 80×25 characters visible at once. Things have changed. I can have two terminals of 119×61 characters each with a fixed width font, and perhaps twice that with proportional. The number of characters it takes to type an identifier in a smart code editor is proportional to the logarithm of the number of identifiers in scope, not to their length. From itz at very.loosely.org Sun Sep 20 20:08:06 2020 From: itz at very.loosely.org (Ian Zimmerman) Date: Sun, 20 Sep 2020 13:08:06 -0700 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> <1600619568.wvzvea6eke.astroid@rebel.none> Message-ID: <20200920200806.u4o5zhagkhng3vup@moyka> On 2020-09-21 00:30, Ignat Insarov wrote: > We have retina screens, GPU accelerated rendering and magical > auto-completion. Who's "we"? 1 and 2 are constrained by money. 1 and 3 are also constrained by personal preference. I need a _big_ screen because I also do photo processing beside coding, and I can't afford 2 screens, one big and another high density. And I use completion only by explicit request (ie. a specific keybinding), because it isn't good enough (and can't be) to be always right, and for me the stress from correcting it when wrong outweighs the benefit of the common case, when right. Veering off-topic, hence redirecting replies to myself. -- Ian From migmit at gmail.com Sun Sep 20 21:06:36 2020 From: migmit at gmail.com (MigMit) Date: Sun, 20 Sep 2020 23:06:36 +0200 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> <1600619568.wvzvea6eke.astroid@rebel.none> Message-ID: <9958596F-B64A-4D4C-847A-1045735DE4CB@gmail.com> My guess is that most of Café members would agree that if given a choice between buttons with short labels and buttons with long labels they would ask to revert back to CLI. Maybe I'm wrong here, but I think Unix-style CLI is not going anywhere any time soon, regardless of retina screens and any other bells and whistles of modern GUIs. Don't get me wrong, GUIs are great for a lot of specific tasks, but CLI still outshines them in many areas, and I think short command names are a part of a reason for that. Not just command names, in fact; in a one-liner I would rather write "while read a; do cp $a...; done" than "while read filename; do cp $filename...; done". They are exactly the same, but it's simply easier to deal with a short line — read it, edit it etc. And, as Haskell makes it easier to use and control local variables, I think it's even more forgiving about short names. In C you'd need a descriptive name simply to make sure you don't use your loop counter somewhere inside the loop; but in Haskell, due to immutability, you don't usually have to worry. > On 20 Sep 2020, at 21:30, Ignat Insarov wrote: > > Command shell is a very old and peculiar human interface — if human at > all. They had 80 character wide screens and visibly lagging connexion. > We have retina screens, GPU accelerated rendering and magical > auto-completion. Imagine those commands were buttons that you could > press. Would you prefer a button to say _«`ls`»_ or _«list files»_, > _«`cd`»_ or _«change directory»_? For a vivid example, imagine a web > site where you have `lgn` and `plrq` instead of _«log in»_ and _«pull > request»_. Would you like that? > > Getting back to Mathematics — this is where abstraction and notation > come in. We can give names to things and we can use scoping. But > neither mathematicians nor system administrators invent new > terminology for every next paper or script — maybe a few key words. > Industrial programming is yet another thing. I imagine when you have a > record with a hundred fields it pays off to have longish field labels. > And you cannot expect the next person to read your code from top to > bottom so that they get used to its peculiar vocabulary. For example, > today I am merging two branches of a 10 thousand lines code base that > diverged last Spring. Guessing the difference between `rslt` and `res` > is the last thing I need. You do not need to call your context > _«`ctx`»_ and your result _«`rslt`»_ — there are already words for it. > > There was a time when every character needed to be typed and there was > a rectangle of only some 80×25 characters visible at once. Things have > changed. I can have two terminals of 119×61 characters each with a > fixed width font, and perhaps twice that with proportional. The number > of characters it takes to type an identifier in a smart code editor is > proportional to the logarithm of the number of identifiers in scope, > not to their length. From jeffbrown.the at gmail.com Sun Sep 20 22:06:18 2020 From: jeffbrown.the at gmail.com (Jeffrey Brown) Date: Sun, 20 Sep 2020 17:06:18 -0500 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: <9958596F-B64A-4D4C-847A-1045735DE4CB@gmail.com> References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> <1600619568.wvzvea6eke.astroid@rebel.none> <9958596F-B64A-4D4C-847A-1045735DE4CB@gmail.com> Message-ID: One's capacity to write (and then be able to read) lots of little functions is greatly helped by a tool that lets you jump to the definition of a term. Emacs + hasktags, for instance. In addition to names, moving as much logic from possible into the types is helpful. You're liable to update a function and leave a stale comment; not so its type signature. With good types and names, I find that almost the only things I need to comment are surprises, pitfalls, gotchas. (And with dependent types I'm sure I'd need fewer of those.) Time spent rewriting with no other goal in mind is, if you're going to keep coming back to the code, generally time well spent. Anything that confuses you when you revisit code deserves at the least a comment, if not rewriting the code itself. Naming *should* be hard. A good name defines for a human in a few words what takes a paragraph to define for the computer. It almost requires a different mindset, a second level of awareness -- less how, more what and why. On Sun, Sep 20, 2020 at 4:08 PM MigMit wrote: > My guess is that most of Café members would agree that if given a choice > between buttons with short labels and buttons with long labels they would > ask to revert back to CLI. Maybe I'm wrong here, but I think Unix-style CLI > is not going anywhere any time soon, regardless of retina screens and any > other bells and whistles of modern GUIs. Don't get me wrong, GUIs are great > for a lot of specific tasks, but CLI still outshines them in many areas, > and I think short command names are a part of a reason for that. > > Not just command names, in fact; in a one-liner I would rather write > "while read a; do cp $a...; done" than "while read filename; do cp > $filename...; done". They are exactly the same, but it's simply easier to > deal with a short line — read it, edit it etc. > > And, as Haskell makes it easier to use and control local variables, I > think it's even more forgiving about short names. In C you'd need a > descriptive name simply to make sure you don't use your loop counter > somewhere inside the loop; but in Haskell, due to immutability, you don't > usually have to worry. > > > On 20 Sep 2020, at 21:30, Ignat Insarov wrote: > > > > Command shell is a very old and peculiar human interface — if human at > > all. They had 80 character wide screens and visibly lagging connexion. > > We have retina screens, GPU accelerated rendering and magical > > auto-completion. Imagine those commands were buttons that you could > > press. Would you prefer a button to say _«`ls`»_ or _«list files»_, > > _«`cd`»_ or _«change directory»_? For a vivid example, imagine a web > > site where you have `lgn` and `plrq` instead of _«log in»_ and _«pull > > request»_. Would you like that? > > > > Getting back to Mathematics — this is where abstraction and notation > > come in. We can give names to things and we can use scoping. But > > neither mathematicians nor system administrators invent new > > terminology for every next paper or script — maybe a few key words. > > Industrial programming is yet another thing. I imagine when you have a > > record with a hundred fields it pays off to have longish field labels. > > And you cannot expect the next person to read your code from top to > > bottom so that they get used to its peculiar vocabulary. For example, > > today I am merging two branches of a 10 thousand lines code base that > > diverged last Spring. Guessing the difference between `rslt` and `res` > > is the last thing I need. You do not need to call your context > > _«`ctx`»_ and your result _«`rslt`»_ — there are already words for it. > > > > There was a time when every character needed to be typed and there was > > a rectangle of only some 80×25 characters visible at once. Things have > > changed. I can have two terminals of 119×61 characters each with a > > fixed width font, and perhaps twice that with proportional. The number > > of characters it takes to type an identifier in a smart code editor is > > proportional to the logarithm of the number of identifiers in scope, > > not to their length. > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -- Jeff Brown | Jeffrey Benjamin Brown LinkedIn | Github | Twitter | Facebook | very old Website -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Sun Sep 20 23:13:59 2020 From: david.feuer at gmail.com (David Feuer) Date: Sun, 20 Sep 2020 19:13:59 -0400 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: One important, sometimes difficult, part is to work out how to break up a big function into multiple documentable small ones. Finding good natural boundaries can be tough, and may require multiple attempts, but it's really great when it works out. Ideally, each piece represents a single idea, which makes independent sense without needing to know how it will be used. Each piece should ideally have a small enough implementation to be able to understand it easily, and should have a reasonably descriptive name. On Sat, Sep 19, 2020, 11:03 AM Misja Alma wrote: > Hi, > > I have been writing Haskell in my spare time for a couple of years now, > but when I showed some code lately to a friend he remarked that he didn't > find it very readable. Actually I agree, when I look at my own code of a > couple of months old I have trouble figuring out too what exactly it is > doing. > > I'm coming from a Java and Scala background and there, especially for > Java, are some generally accepted best practices that make sure that your > teammates don't have too much trouble reading your code. E.g. write short > functions with a single responsibility, use variable, class and function > names that explain what they are meant for, etc. > > I think some of those best practices, like short functions with single > responsibility, are useful for Haskell as well. But Haskell is a different > language than Java and has its own strong points and pitfalls regarding > readability, so it probably needs different coding standards as well. > > I have been looking on the Internet if I could find some tips about > improving readability but all I could find was > http://www.haskellforall.com/. Although there are some useful tips in > there, this site seems to be aimed at making Haskell easier to read for > newcomers from other languages. What I am interested in are tips from real > projects that are built by real teams. > Does anybody have any tips, or are there some sites or books that I could > read about this topic? > > Thanks, > Misja > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From ietf-dane at dukhovni.org Sun Sep 20 23:15:52 2020 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Sun, 20 Sep 2020 19:15:52 -0400 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: <20200919205727.GJ64864@straasha.imrryr.org> Message-ID: <20200920231552.GM64864@straasha.imrryr.org> On Sun, Sep 20, 2020 at 08:37:28PM +0200, MarLinn wrote: > > main :: IO () > > main = Q.getContents -- raw bytes > > & AS.parsed lineParser -- stream of parsed `Maybe Int`s; blank lines are `Nothing` > > & void -- drop any unparsed nonsense at the end -- [1] > > & S.split Nothing -- split on blank lines > > & S.maps S.concat -- keep `Just x` values in the sub-streams (cp. catMaybes) > > & S.mapped S.sum -- sum each substream > > & S.print -- stream results to stdout > > There's still quite a bit that can be improved here. Yes, that short example (from a module's documentation) was chosen to illustrate left-to-right style, and the direct use of helper functions from `streaming` also helps to illustrate those functions in use. So in the context of module documentation, it is actually helpful to not replace the helpers with semantically appropriate (but implementation opaque) aliases. As for objections to use of left to right notation, in representing chains of transformations, we're going to have to disagree about that. -- Viktor. From mlang at delysid.org Sun Sep 20 23:29:32 2020 From: mlang at delysid.org (Mario Lang) Date: Mon, 21 Sep 2020 01:29:32 +0200 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: (Ignat Insarov's message of "Mon, 21 Sep 2020 00:30:23 +0500") References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> <1600619568.wvzvea6eke.astroid@rebel.none> Message-ID: <87zh5k6n8j.fsf@blind.guru> Ignat Insarov writes: > Command shell is a very old and peculiar human interface — if human at > all. They had 80 character wide screens and visibly lagging connexion. > We have retina screens, GPU accelerated rendering and magical > auto-completion. You might have that. I, as a blind braille display user, have one line of at most 80 characters. When I am traveling, I typically only have 40 characters. Given these constraints, I still consider a command line interface superior to any sort of buttonized nonesense. But thats just me, apparently. -- CYa, ⡍⠁⠗⠊⠕ From qdunkan at gmail.com Mon Sep 21 00:27:20 2020 From: qdunkan at gmail.com (Evan Laforge) Date: Sun, 20 Sep 2020 17:27:20 -0700 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> Message-ID: On Fri, Sep 18, 2020 at 12:48 AM Simon Peyton Jones wrote: > > | For explicit exports, I often leave them off for convenience during > | development, but put them in when it settles down. I don't think it > | unlocks any optimization in code generation > > Actually, it does make a difference to optimisation. If a function is known not to be exported, then GHC knows every one of its call sites. Eg so I stand corrected! I recall reading somewhere that, for the purposes of inlining, all symbols inside the module are fully visible, so the only difference the export makes is whether or not the non-inlined/specialized version is also kept around for possible external callers. So maybe that's just wrong, or I made it up somehow, or maybe it's right but just for inlining and not for those other things? I suppose it could theoretically have been right if GHC were willing to duplicate all exported functions and perhaps analyze them twice, but perhaps it's not willing to do that? I'm not saying it should, especially if it would hurt compile time, just curious. From ben.franksen at online.de Mon Sep 21 06:53:54 2020 From: ben.franksen at online.de (Ben Franksen) Date: Mon, 21 Sep 2020 08:53:54 +0200 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: Am 20.09.20 um 14:02 schrieb Ignat Insarov: > The Darcs code you show illustrates the point Chris Done speaks for as > well. Observe top level names: `displayPatch`, `commuteConflicting`, > `cleanMerge` — quite German! Yes, top level functions are typical candidates for longer names. I am not opposed to the "german notation" in any way, I just don't think it is always appropriate to use this style for every variable, including function parameters, as suggested in the blog. > Then there is `ctxAddInvFL` and > `mapFL_FL`, but that from other modules. Well, sometimes you have to compromise between legibility and conciseness, especially when distinguishing between variants. The FL and RL sequence types are ubiquitous in our code base and the convention of suffixing a function with them to indicate what type of parameter it takes is well established. I wouldn't want to write out "monoidConcat" instead of "mconcat" everywhere. (Or would that have to be "semigroupConcat" nowadays? Thankfully we could avoid bikeshedding this to death...) Or "foldRight" or even "foldAssociatingRightwards" instead of "foldr". > Finally, I tried to find out > what `Prim` stands for — I went as far as to the index of `darcs` on > Hackage[3] but no luck. And `prim` is the most frequent in the list of > words of the module, with 125 occurrences in normalized case. > Primitive? Primary? Prime? Primavera? Your first guess was correct ;-) Though I doubt that knowing this helps to understand the code better. Knowing that 'log' is short for 'logarithm' doesn't help you understand a formula containing 'log' unless you already know what 'logarithm' means. Long "german" names don't relieve you from the task of familiarizing yourself with the problem domain and its concepts and conventions. As regards type setting and unicode symbols, I am not a great fan of that stuff. IMO it makes no sense to mimic mathematical style in any literal sense. The point of a formula is not that it contains fancy special notation. Rather, the point is to avoid distracting the reader with irrelevant details. The only difference between a mathematical formula and a (functional) program is that the latter can be (efficiently) executed by a machine, *in addition* to being read and understood by humans. Besides, a lot of notational conventions in mathematics are not well suited to programming or formally proving things. Many (if not most) constructs that traditionally have special notation in math (e.g. sum, integral, etc) are subsumed by the concept of a higher order function. This has been well-known for several decades now, but the mathematical community is extremely conservative with its established notation. My personal explanation for this phenomenon is that all the existing work in math (books, papers) serve as a giant "standard library for the math language" and changing established notation would mean a huge effort in factorizing (i.e. re-writing) most of that existing work. That said, there are cases where a graphical notation is actually better suited for abstracting irrelevant details than the equivalent textual formula. The most well-known example for this is category theory with its arrow diagrams. As I found out a few years ago, patch theory is another instance where an arrow diagram is often more succinct and less cluttered than the textual formula. Ever since I wished I could include such diagrams directly in the code. Here is an example where I used ASCII graphics to explain a fairly complicated piece of code: https://hub.darcs.net/darcs/darcs-screened/browse/src/Darcs/Repository/Merge.hs#95 This crutch clearly has limits: to picture a three-way merge you need to move from squares to cubes which gets quite annoying to do in ASCII. Cheers Ben From simonpj at microsoft.com Mon Sep 21 07:41:43 2020 From: simonpj at microsoft.com (Simon Peyton Jones) Date: Mon, 21 Sep 2020 07:41:43 +0000 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> Message-ID: | I suppose it could theoretically have been right if GHC were willing | to duplicate all exported functions and perhaps analyze them twice, | but perhaps it's not willing to do that? I'm not saying it should, | especially if it would hurt compile time, just curious. For non-exported functions, GHC inlines one regardless of size (and discards the definition) if it has exactly one occurrence, because that doesn't duplicate. For all other functions (local and exported) GHC will inline (and hence duplicate) small ones, and not inline (thereby avoiding duplicating) big ones. There are flags to control what "big" means. Does that answer your question? Simon | -----Original Message----- | From: Evan Laforge | Sent: 21 September 2020 01:27 | To: Simon Peyton Jones | Cc: Isaac Elliott ; Olaf Klinke ; Haskell Cafe | Subject: Re: [Haskell-cafe] Are explicit exports and local imports | desirable in a production application? | | On Fri, Sep 18, 2020 at 12:48 AM Simon Peyton Jones | wrote: | > | > | For explicit exports, I often leave them off for convenience | during | > | development, but put them in when it settles down. I don't think | it | > | unlocks any optimization in code generation | > | > Actually, it does make a difference to optimisation. If a function | is known not to be exported, then GHC knows every one of its call | sites. Eg so | | I stand corrected! | | I recall reading somewhere that, for the purposes of inlining, all | symbols inside the module are fully visible, so the only difference | the export makes is whether or not the non-inlined/specialized version | is also kept around for possible external callers. So maybe that's | just wrong, or I made it up somehow, or maybe it's right but just for | inlining and not for those other things? | | I suppose it could theoretically have been right if GHC were willing | to duplicate all exported functions and perhaps analyze them twice, | but perhaps it's not willing to do that? I'm not saying it should, | especially if it would hurt compile time, just curious. From compl.yue at icloud.com Mon Sep 21 08:18:33 2020 From: compl.yue at icloud.com (YueCompl) Date: Mon, 21 Sep 2020 16:18:33 +0800 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: I'd like to understand this issue from the psychological aspect, that readable doesn't mean reasonable that much, while being readable well serves Illusion of control [1] of projects under management, for bosses in Bullshit Jobs[2], we actually need to be in flow state [3] to properly reason about (including reading) an important piece of code. It's not possible with just trivial effort or even in a hurry, even you wrote the code yourself, if not in resonance with the mindset behind the code. Within many (usually large) corporate codebase, you can easily read any small unit of code, so as to perceive that all of them are doing really useful things for each's own purpose, but very hard to discover how business goals can be fulfilled by the whole codebase. So long as business consultants and stakeholders don't speak a programming language, code in our PL won't express business logics directly. I had assumed the role of a translator between programmers and business users earlier in my career, i.e. doing requirement analysis & design implementation per ad-hoc basis, I would suggest that there are really big gaps. I love Haskell and I'd like to say, I don't expect Haskell code being easy to read, but for truly effective definitions of the problem and the solution, Haskell code should be really easier to reason about with the ultimate goal in mind. [1] https://en.wikipedia.org/wiki/Illusion_of_contro [2] https://en.wikipedia.org/wiki/Bullshit_Jobs [3] https://en.wikipedia.org/wiki/Flow_(psychology) Compl > On 2020-09-19, at 23:02, Misja Alma > wrote: > > Hi, > > I have been writing Haskell in my spare time for a couple of years now, but when I showed some code lately to a friend he remarked that he didn't find it very readable. Actually I agree, when I look at my own code of a couple of months old I have trouble figuring out too what exactly it is doing. > > I'm coming from a Java and Scala background and there, especially for Java, are some generally accepted best practices that make sure that your teammates don't have too much trouble reading your code. E.g. write short functions with a single responsibility, use variable, class and function names that explain what they are meant for, etc. > > I think some of those best practices, like short functions with single responsibility, are useful for Haskell as well. But Haskell is a different language than Java and has its own strong points and pitfalls regarding readability, so it probably needs different coding standards as well. > > I have been looking on the Internet if I could find some tips about improving readability but all I could find was http://www.haskellforall.com/ . Although there are some useful tips in there, this site seems to be aimed at making Haskell easier to read for newcomers from other languages. What I am interested in are tips from real projects that are built by real teams. > Does anybody have any tips, or are there some sites or books that I could read about this topic? > > Thanks, > Misja > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From kindaro at gmail.com Mon Sep 21 10:05:13 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Mon, 21 Sep 2020 15:05:13 +0500 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: <871riw81x3.fsf@blind.guru> References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> <1600619568.wvzvea6eke.astroid@rebel.none> <871riw81x3.fsf@blind.guru> Message-ID: Hello Ian and Mario. > > We have retina screens, GPU accelerated rendering and magical > > auto-completion. > > Who's "we"? 1 and 2 are constrained by money. 1 and 3 are also > constrained by personal preference. > > I need a _big_ screen because I also do photo processing beside coding, > and I can't afford 2 screens, one big and another high density. > > Command shell is a very old and peculiar human interface — if human at > > all. They had 80 character wide screens and visibly lagging connexion. > > We have retina screens, GPU accelerated rendering and magical > > auto-completion. > > You might have that. I, as a blind braille display user, > have one line of at most 80 characters. When I am traveling, I > typically only have 40 characters. Given these constraints, I still > consider a command line interface superior to any sort of buttonized > nonesense. But thats just me, apparently. This is not how I expected my message to be understood. I did not mean to imply that every Haskell programmer has or should have a retina screen and a high performance GPU — only that, as a profession, we have way better tools now than back then. In humanities, it is usual for there to be either a normal distribution or a Pareto distribution in any large enough sample of data. So, unlike in precise sciences, a counter-example does not refute a proposition. What matters is that there is a trend. And there is a trend associated with larger and finer displays. It dawns on even the most _«old school»_ people by now. See for example a letter on Linux Kernel Mailing List.[1] > When I tile my terminal windows on my display, I can have 6 terminals > visible at one time, and that's because I have them three wide. And I > could still fit 80% of a fourth one side-by-side. > > And guess what? That's with my default "100x50" terminal window (go to > your gnome terminal settings, you'll find that the 80x25 thing is just > an initial default that you can change), not with some 80x25 one. And > that's with a font that has anti-aliasing and isn't some pixelated > mess. I have no specific insight about how to optimize readability for people that listen to code[2] or sense it with their hands. What I am sure of is that there are better ways to go about it than dwelling in the past. [1]: https://lkml.org/lkml/2020/5/29/1038 [2]: https://www.vincit.fi/fi/software-development-450-words-per-minute/ From qdunkan at gmail.com Mon Sep 21 10:29:49 2020 From: qdunkan at gmail.com (Evan Laforge) Date: Mon, 21 Sep 2020 03:29:49 -0700 Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> Message-ID: On Mon, Sep 21, 2020 at 12:41 AM Simon Peyton Jones wrote: > > | I suppose it could theoretically have been right if GHC were willing > | to duplicate all exported functions and perhaps analyze them twice, > | but perhaps it's not willing to do that? I'm not saying it should, > | especially if it would hurt compile time, just curious. > > For non-exported functions, GHC inlines one regardless of size (and discards the definition) if it has exactly one occurrence, because that doesn't duplicate. For all other functions (local and exported) GHC will inline (and hence duplicate) small ones, and not inline (thereby avoiding duplicating) big ones. > > There are flags to control what "big" means. > > Does that answer your question? Yes I think it does, to rephrase, we could say the distinction is not so much exported or not, but 1 caller vs. >1 caller. But of course it happens that exported forces you to assume >1 caller. I was thinking GHC could theoretically be willing to duplicate an exported oversized function if it only occurs once within the module, because this is a max of 1 additional copy, not an unbounded number. But perhaps it makes sense that it refuses to copy at all if it's over the size limit, because who's to say how far over the limit it is! Inlining something with exactly 1 caller is guaranteed safe. It also makes it worry-free to extract an expression to a where, or promote a where to the toplevel, so long as it remains called only once. This is as it should be, but it's nice to know for sure! From kindaro at gmail.com Mon Sep 21 12:20:20 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Mon, 21 Sep 2020 17:20:20 +0500 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: Message-ID: > Well, sometimes you have to compromise between legibility and > conciseness, especially when distinguishing between variants. The FL and > RL sequence types are ubiquitous in our code base and the convention of > suffixing a function with them to indicate what type of parameter it > takes is well established. I wouldn't want to write out "monoidConcat" > instead of "mconcat" everywhere. (Or would that have to be > "semigroupConcat" nowadays? Thankfully we could avoid bikeshedding this > to death...) Or "foldRight" or even "foldAssociatingRightwards" instead > of "foldr". My proposition is twofold and this is a perfect illustration for both its sides. 1. `mconcat` is an example of a thing that benefits from mathematical style. Please by all means shorten it to death and standardize. Take it from 7 symbols to 1 — best a beautiful pictogram. Same for `foldr`. > As regards type setting and unicode symbols, I am not a great fan of > that stuff. > > IMO it makes no sense to mimic mathematical style in any literal sense. > The point of a formula is not that it contains fancy special notation. It is not about what you think, it is about what you think others think. Quoting Leonardo Da Vinci: > It seems to me no small grace in a painter to be able to give a pleasing air > to his figures, and this grace, if he have it not by nature, he may acquire > it by incidental study in this way: Look about you and take the best parts > of many beautiful faces, of which the beauty is established rather by public > fame than by your own judgement; for you may deceive yourself and select > faces which bear a resemblance to your own, since it would often seem that > such resemblance pleases us; and if you were ugly you would select faces > that are not beautiful, and you would then create ugly faces as many > painters do. For often a master's shapes resemble himself; so therefore > select beauties as I tell you and fix them in your mind. That is, you should make sure that your judgement of beauty is independent of your own opinion. The effort of type setters and font designers is not vain. Even if you cannot detect the difference, you can guess that it is there by observing the choices of others. One famous programmer even went as far as to design a type setting language, I am sure you know his name. > Besides, a lot of notational conventions in mathematics are not well > suited to programming or formally proving things. Many (if not most) > constructs that traditionally have special notation in math (e.g. sum, > integral, etc) are subsumed by the concept of a higher order function. > This has been well-known for several decades now, but the mathematical > community is extremely conservative with its established notation. … I am sure the functional programming community deserves some special symbols of our own. I know research papers introduce various shorthand conventions for folds and such. Question is, whether we can find it in ourselves to converge on a notation. 2. `ctxAddInvFL` is an example of a thing that benefits from English prose style. I have a speech about it. There are things that are well established and at the same time not justifiable. As a special case, use may outlive justification. My proposition is that creative shortening was justified back then, but now is a relic. Benefits diminished, while drawbacks are still the same: * Harder to invent names. In addition to coming up with a short explanatory English phrase, you have to invent a plausible shortening — extra work. * Harder to read. As you see, it is virtually impossible to determine what `Prim` stands for, and that is an easy example. Even in a suggestive context, it is a tax on the mind of the reader. It is hard enough to learn English. * A trtr of lngstc snsblty. Try wrtn a blg pst ths way & see wht rdrshp u get. To continue from the previous point — it is hard enough to learn English, why should one be forced to learn an English to read prose and an Ngl to read code? I would rather not, if only out of æsthetic sense. I am sure one gets used to it. And I imagine it becomes faster to read and write the code in a given project once you internalize the shortenings. So then it is an optimization for those _«in»_ and a penalty for those _«out»_, thus setting up a _«walled garden»_. Is that ever wanted? From ben.franksen at online.de Mon Sep 21 19:36:01 2020 From: ben.franksen at online.de (Ben Franksen) Date: Mon, 21 Sep 2020 21:36:01 +0200 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> <1600619568.wvzvea6eke.astroid@rebel.none> <871riw81x3.fsf@blind.guru> Message-ID: Am 21.09.20 um 12:05 schrieb Ignat Insarov: > I did not mean to imply that every Haskell programmer has or should > have a retina screen and a high performance GPU — only that, as a > profession, we have way better tools now than back then. > > In humanities, it is usual for there to be either a normal > distribution or a Pareto distribution in any large enough sample of > data. So, unlike in precise sciences, a counter-example does not > refute a proposition. What matters is that there is a trend. And there > is a trend associated with larger and finer displays. It dawns on even > the most _«old school»_ people by now. See for example a letter on > Linux Kernel Mailing List.[1] What you and Mr. Torvalds forget is that there is a reason why newspapers are written in relatively small columns. Even scientific papers are often printed in two column mode. Typesetting has been done since a few 100 years and has for the most time been an analog technique, so it mostly wasn't limited by available resolution. The point is that humans aren't good at reading text when the line length exceeds roughly 80 characters because when you jump from the far right to the far left of the text, it gets hard to correctly identify where the next line starts. A value of 60 is ideal for non-indented text. Thus considering indentation that is not too deep, say (usually) not exceeding 5 levels a 4 spaces makes 20 chars, which means max. line length is ideal at 80 and should not exceed 100 (or 90 with 2 spaces for indentation). I can view files in fullscreen mode if forced to do so by the author of the code but I hate it. This has nothing to do with being old-school. >> When I tile my terminal windows on my display, I can have 6 terminals >> visible at one time, and that's because I have them three wide. And I >> could still fit 80% of a fourth one side-by-side. Using a laptop with max. 15 inch display? How old are you? People over 50 years or so usually have trouble reading such small print, no matter how great the resolution. So (apart from the arguments above) screen size often limits how much text can be displayed in a readable fashion. Cheers Ben From kindaro at gmail.com Mon Sep 21 20:39:29 2020 From: kindaro at gmail.com (Ignat Insarov) Date: Tue, 22 Sep 2020 01:39:29 +0500 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> <1600619568.wvzvea6eke.astroid@rebel.none> <871riw81x3.fsf@blind.guru> Message-ID: > What you and Mr. Torvalds forget is that there is a reason why > newspapers are written in relatively small columns. Even scientific > papers are often printed in two column mode. Typesetting has been done > since a few 100 years and has for the most time been an analog > technique, so it mostly wasn't limited by available resolution. The > point is that humans aren't good at reading text when the line length > exceeds roughly 80 characters because when you jump from the far right > to the far left of the text, it gets hard to correctly identify where > the next line starts. A value of 60 is ideal for non-indented text. Thus > considering indentation that is not too deep, say (usually) not > exceeding 5 levels a 4 spaces makes 20 chars, which means max. line > length is ideal at 80 and should not exceed 100 (or 90 with 2 spaces for > indentation). > > I can view files in fullscreen mode if forced to do so by the author of > the code but I hate it. This has nothing to do with being old-school. This is a fair objection. Absurdly long lines are uncomfortable. In defense, we have: - Syntax highlighting. - Blank lines. - Indentation. - Ragged right edge. - Infinite canvas. This makes lines multiform, so losing the line seems less likely. Further, code is not read like prose — you skim much more, the zig-zag pattern of the usual reading is gone. It seems to me that all this together gives enough freedom that shortening identifiers is not a necessity anymore. I am not aware of any research that substantiates this opinion though. > >> When I tile my terminal windows on my display, I can have 6 terminals > >> visible at one time, and that's because I have them three wide. And I > >> could still fit 80% of a fourth one side-by-side. > > Using a laptop with max. 15 inch display? How old are you? People over > 50 years or so usually have trouble reading such small print, no matter > how great the resolution. So (apart from the arguments above) screen > size often limits how much text can be displayed in a readable fashion. Linus Torvalds is exactly 50. In any case, this is all adjustable. I zoom in when my eyes are tired. From Andrew.Butterfield at scss.tcd.ie Tue Sep 22 13:48:52 2020 From: Andrew.Butterfield at scss.tcd.ie (Andrew Butterfield) Date: Tue, 22 Sep 2020 14:48:52 +0100 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> <1600619568.wvzvea6eke.astroid@rebel.none> <871riw81x3.fsf@blind.guru> Message-ID: <51422AAB-9DC0-41C2-96C6-5615A7BABA59@scss.tcd.ie> +1 I am working on a project looking at formal verfication for an open-source real-time operating system called RTEMS - rtems.org . I use Haskell to do rapid prototyping of various tools we are building, ultimately to be delivered in python. RTEMS coding standards for both C/C++ and Python mandate a maximum line length of 80, for pretty much the reasons stated by Ben below Regards, Andrew > On 21 Sep 2020, at 20:36, Ben Franksen wrote: > > Am 21.09.20 um 12:05 schrieb Ignat Insarov: >> I did not mean to imply that every Haskell programmer has or should >> have a retina screen and a high performance GPU — only that, as a >> profession, we have way better tools now than back then. >> >> In humanities, it is usual for there to be either a normal >> distribution or a Pareto distribution in any large enough sample of >> data. So, unlike in precise sciences, a counter-example does not >> refute a proposition. What matters is that there is a trend. And there >> is a trend associated with larger and finer displays. It dawns on even >> the most _«old school»_ people by now. See for example a letter on >> Linux Kernel Mailing List.[1] > > What you and Mr. Torvalds forget is that there is a reason why > newspapers are written in relatively small columns. Even scientific > papers are often printed in two column mode. Typesetting has been done > since a few 100 years and has for the most time been an analog > technique, so it mostly wasn't limited by available resolution. The > point is that humans aren't good at reading text when the line length > exceeds roughly 80 characters because when you jump from the far right > to the far left of the text, it gets hard to correctly identify where > the next line starts. A value of 60 is ideal for non-indented text. Thus > considering indentation that is not too deep, say (usually) not > exceeding 5 levels a 4 spaces makes 20 chars, which means max. line > length is ideal at 80 and should not exceed 100 (or 90 with 2 spaces for > indentation). > > I can view files in fullscreen mode if forced to do so by the author of > the code but I hate it. This has nothing to do with being old-school. > >>> When I tile my terminal windows on my display, I can have 6 terminals >>> visible at one time, and that's because I have them three wide. And I >>> could still fit 80% of a fourth one side-by-side. > > Using a laptop with max. 15 inch display? How old are you? People over > 50 years or so usually have trouble reading such small print, no matter > how great the resolution. So (apart from the arguments above) screen > size often limits how much text can be displayed in a readable fashion. > > Cheers > Ben > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------------------------------------------------------------- Andrew Butterfield Tel: +353-1-896-2517 Fax: +353-1-677-2204 Lero at TCD, Head of Software Foundations & Verification Research Group School of Computer Science and Statistics, Room G.39, O'Reilly Institute, Trinity College, University of Dublin http://www.scss.tcd.ie/Andrew.Butterfield/ -------------------------------------------------------------------- -------------- next part -------------- An HTML attachment was scrubbed... URL: From will.g.bush at gmail.com Tue Sep 22 19:54:46 2020 From: will.g.bush at gmail.com (Will Bush) Date: Tue, 22 Sep 2020 14:54:46 -0500 Subject: [Haskell-cafe] Readable Haskell In-Reply-To: <51422AAB-9DC0-41C2-96C6-5615A7BABA59@scss.tcd.ie> References: <9BE39A03-4EEA-4FA6-8E6E-821A10015299@gmail.com> <1600619568.wvzvea6eke.astroid@rebel.none> <871riw81x3.fsf@blind.guru> <51422AAB-9DC0-41C2-96C6-5615A7BABA59@scss.tcd.ie> Message-ID: > I use Haskell to do rapid prototyping of various tools we are building, ultimately to be delivered in python. Off topic: Do you feel the code comes out better when you do it that way? I'm going to share a story I've shared a couple of times, but no one really saw it. I wish it was about a more real word application, but unfortunately I haven't had the opportunity to write those in more than one language to compare and contrast. I took an intro to game programming class in college as an elective for fun and the professor had us implement this old text based game called Hunt the Wumpus 3 times. The first time was text based, the 2nd time was 2D, and the 3rd was 3D (aside from a semester-long group project). I remember the instructions mandating that it is sufficiently OO (object oriented). I didn't think anything of it at the time and everything seemed fine. Note we using C# at the time because later we were going to use a framework like Microsoft's XNA. I ended up using Monogame which is based on XNA Framework. Basically it's a lower level framework compared to something like Unity. One thing that stood out to me was I was unable to reuse anything from the text based game in the 2D one because it had IO intermingled in the code and depended on blocking to get input from the user. The 2D version was using a game loop and was more event driven. That was my first time doing anything like that and it ended up a buggy mess that barely worked right. The third time in 3D I think I pretty much had to start from scratch again, but I got better at the event driven nature of the thing and it turned out ok (it worked). Fast forward in time and I am learning Rust for fun and decide to implement that same text based game because I want to know what it feels like to program in that language vs C#. I thought it turned out was great until I did the same game in Haskell. Still blows my mind today how that language forced me to think about things differently. If I remember right the Haskell solution was 1/3 the code (measured with tokei) (2 source files and 1 test file vs about 9 source files and 4 test files). All the IO ended up near the main function instead of spread out all over the place, and it's not just concise syntax that reduced the amount of code, but a simpler solution in my opinion. The game logic became just a bunch of pure functions in a small library that moved the game from state to state in an immutable game object. I haven't tried it, but I imagine I would have been able to reuse the game logic in a 2D game because the logic wasn't coupled to and dependent on IO blocking. I think the purely functional nature of Haskell, especially the type discipline around IO effects, drove me to a better solution. It's kind of funny in in retrospect I find it hard not to just blame myself for over-complicating it. Some of that does come down to lack of experience at the time. However, I work professionally in C# and most of the code I see seems like an over-complicated zoo with IO and partial functions everywhere. If your interested you can judge it for yourself: C# text based: https://github.com/willbush/hunt-the-wumpus C# 3D with primitive shapes: https://github.com/willbush/hunt-the-wumpus-3d Rust text based: https://github.com/willbush/wump Haskell text based: https://github.com/willbush/hwump I have the 2D code in a private repo because it's such a train wreck that I didn't want anyone to see it. I really don't care anymore so I can open-source it if anyone reads this far and cares to see it. I later realized the architecture I ran across is basically this: https://blog.ploeh.dk/2016/03/18/functional-architecture-is-ports-and-adapters/ On Tue, Sep 22, 2020 at 1:40 PM Andrew Butterfield < Andrew.Butterfield at scss.tcd.ie> wrote: > +1 > > I am working on a project looking at formal verfication for an open-source > real-time operating system called RTEMS - rtems.org. I use Haskell to do > rapid prototyping of various tools we are building, ultimately to be > delivered in > python. > > RTEMS coding standards for both C/C++ and Python mandate a maximum line > length of 80, > for pretty much the reasons stated by Ben below > > Regards, Andrew > > On 21 Sep 2020, at 20:36, Ben Franksen wrote: > > Am 21.09.20 um 12:05 schrieb Ignat Insarov: > > I did not mean to imply that every Haskell programmer has or should > have a retina screen and a high performance GPU — only that, as a > profession, we have way better tools now than back then. > > In humanities, it is usual for there to be either a normal > distribution or a Pareto distribution in any large enough sample of > data. So, unlike in precise sciences, a counter-example does not > refute a proposition. What matters is that there is a trend. And there > is a trend associated with larger and finer displays. It dawns on even > the most _«old school»_ people by now. See for example a letter on > Linux Kernel Mailing List.[1] > > > What you and Mr. Torvalds forget is that there is a reason why > newspapers are written in relatively small columns. Even scientific > papers are often printed in two column mode. Typesetting has been done > since a few 100 years and has for the most time been an analog > technique, so it mostly wasn't limited by available resolution. The > point is that humans aren't good at reading text when the line length > exceeds roughly 80 characters because when you jump from the far right > to the far left of the text, it gets hard to correctly identify where > the next line starts. A value of 60 is ideal for non-indented text. Thus > considering indentation that is not too deep, say (usually) not > exceeding 5 levels a 4 spaces makes 20 chars, which means max. line > length is ideal at 80 and should not exceed 100 (or 90 with 2 spaces for > indentation). > > I can view files in fullscreen mode if forced to do so by the author of > the code but I hate it. This has nothing to do with being old-school. > > When I tile my terminal windows on my display, I can have 6 terminals > visible at one time, and that's because I have them three wide. And I > could still fit 80% of a fourth one side-by-side. > > > Using a laptop with max. 15 inch display? How old are you? People over > 50 years or so usually have trouble reading such small print, no matter > how great the resolution. So (apart from the arguments above) screen > size often limits how much text can be displayed in a readable fashion. > > Cheers > Ben > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. > > > -------------------------------------------------------------------- > Andrew Butterfield Tel: +353-1-896-2517 Fax: +353-1-677-2204 > Lero at TCD, Head of Software Foundations & Verification Research Group > School of Computer Science and Statistics, > Room G.39, O'Reilly Institute, Trinity College, University of Dublin > http://www.scss.tcd.ie/Andrew.Butterfield/ > -------------------------------------------------------------------- > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From dominikbollmann at gmail.com Wed Sep 23 07:15:40 2020 From: dominikbollmann at gmail.com (Dominik Bollmann) Date: Wed, 23 Sep 2020 09:15:40 +0200 Subject: [Haskell-cafe] Open Kattis Problem Srednji: Hints to improve my algorithm Message-ID: <87y2l1arqb.fsf@t450s> Hi Haskell-Cafe, I've been trying to solve the Open Kattis Problem called Srednji recently, unfortunately without success. Given a sequence A and a number B within that sequence this problem asks to find all odd sub-sequences of A that, when sorted, have B as their median in the middle. That is, from A we may remove some prefix and/or suffix and if the resulting sub-sequence -- when sorted -- contains B in the middle, then this sub-sequence is a solution. The problem asks to find the number of all solutions. Check out https://open.kattis.com/problems/srednji for the details. My Haskell solution below tries to find the number of odd sub-sequences by first locating the median and then repeatedly moving left and right from that median to find larger and larger sub-sequence candidates. Each found candidate is checked to have B in the middle when sorted in order to become a solution. Moreover, I also extend each such candidate further to the left (and to the right, respectively) to determine whether these leftward or rightward extensions are solutions, too. I think with this approach I systematically enumerate all solutions. Unfortunately, though, this approach is too slow and times out on the 11th hidden test cases. I'd therefore be thankful for hints about different approaches to solving this problem more efficiently. Thanks! Dominik. ==================================================================== My current, slow Haskell code is this: import Data.Maybe import Data.Sequence (Seq, (<|), (|>)) import qualified Data.Sequence as Seq import qualified Data.Vector.Unboxed as Vec data SubSeq = SubSeq { getBalance :: {-# UNPACK #-} !Int , getSubSeq :: Seq Int , from :: {-# UNPACK #-} !Int , to :: {-# UNPACK #-} !Int } balancedSubSeqs :: [Int] -> Int -> [SubSeq] balancedSubSeqs seq med = do candidate <- leftRight [val0] let lefts = leftLeft candidate [] rights = rightRight candidate [] candidate ?: lefts ++ rights where medidx = fromJust (Vec.findIndex (== med) arr) val0 = SubSeq 0 (Seq.singleton med) medidx medidx arr = Vec.fromList seq leftRight cands@(SubSeq balance seq i j : _) | i-1 < 0 || j+1 >= Vec.length arr = cands | otherwise = let v1 = arr Vec.! (i-1) v2 = arr Vec.! (j+1) balance' = newBalance balance v1 v2 seq' = (v1 <| seq) |> v2 in leftRight (SubSeq balance' seq' (i-1) (j+1) : cands) leftLeft cand@(SubSeq balance seq i j) sols | i-2 < 0 = sols | otherwise = let v1 = arr Vec.! (i-2) v2 = arr Vec.! (i-1) balance' = newBalance balance v1 v2 seq' = v1 <| v2 <| seq newCand = SubSeq balance' seq' (i-2) j in leftLeft newCand (newCand ?: sols) rightRight cand@(SubSeq balance seq i j) sols | j+2 >= Vec.length arr = sols | otherwise = let v1 = arr Vec.! (j+1) v2 = arr Vec.! (j+2) balance' = newBalance balance v1 v2 seq' = seq |> v1 |> v2 newCand = SubSeq balance' seq' i (j+2) in rightRight newCand (newCand ?: sols) newBalance old n1 n2 | n1 < med, n2 < med = old - 2 | n1 > med, n2 > med = old + 2 | otherwise = old infixr 5 ?: --(?:) :: SubSeq -> [SubSeq] -> [SubSeq] x@(SubSeq b _ _ _) ?: xs | b == 0 = x : xs | otherwise = xs main :: IO () main = do [len, median] <- fmap read . words <$> getLine seq <- fmap read . words <$> getLine let solutions = balancedSubSeqs seq median print (length solutions) From ietf-dane at dukhovni.org Wed Sep 23 07:57:33 2020 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Wed, 23 Sep 2020 03:57:33 -0400 Subject: [Haskell-cafe] Open Kattis Problem Srednji: Hints to improve my algorithm In-Reply-To: <87y2l1arqb.fsf@t450s> References: <87y2l1arqb.fsf@t450s> Message-ID: <20200923075733.GD64864@straasha.imrryr.org> On Wed, Sep 23, 2020 at 09:15:40AM +0200, Dominik Bollmann wrote: > Check out https://open.kattis.com/problems/srednji for the details. Which gives a more precise statement of the problem. > My Haskell solution below tries to find the number of odd sub-sequences > by first locating the median and then repeatedly moving left and right > from that median to find larger and larger sub-sequence candidates. Each > found candidate is checked to have B in the middle when sorted in order > to become a solution. Moreover, I also extend each such candidate > further to the left (and to the right, respectively) to determine > whether these leftward or rightward extensions are solutions, too. > > I think with this approach I systematically enumerate all solutions. > Unfortunately, though, this approach is too slow and times out on the > 11th hidden test cases. > > I'd therefore be thankful for hints about different approaches to > solving this problem more efficiently. This is not really a programming problem, writing the code is the easy part. Rather, this is an *algorithm* problem. An efficient solution uses a better algorithm, not a different implementation of the same algorithm. Your algorithm is not efficient. Forget Haskell for the moment, can you think of a better algorithm. The above algorithm is subtantially slower than optimal. The best algorithm that comes to mind runs in linear time in the length of the list, and requires linear (2N) additional space. No sorting (that would not be linear) or complex testing of candidates is required, just some counting and O(N) book-keeping. -- Viktor. From lemming at henning-thielemann.de Wed Sep 23 09:39:36 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Wed, 23 Sep 2020 11:39:36 +0200 (CEST) Subject: [Haskell-cafe] Are explicit exports and local imports desirable in a production application? In-Reply-To: References: <1e94f713522672730ed293cc87b3c49196df5c47.camel@aatal-apotheke.de> Message-ID: On Fri, 18 Sep 2020, Isaac Elliott wrote: > I don't think that it's unreasonable in general to expect people to > explore a codebase via IDE tooling. But given Haskell's current > situation on that front, I currently agree with your approach to Haskell > imports/exports. I already lost many hours exploring orphaned codebases. Imagine a five year old package or say ten years that you cannot compile anymore. You do not know with what version of GHC it worked, and even if you would know, you will not be able to get this GHC version running anymore. You do not know which library versions the package used, because Build-Depends is missing version ranges. Even if you would know, you would not be able to get old library versions compiled with new GHC versions anymore. This orphaned package uses identifiers like "prim" and you have to search whether this is a local identifier or whether it is external and then from which package? No IDE will help with code that misses so many information. From byorgey at gmail.com Wed Sep 23 10:15:23 2020 From: byorgey at gmail.com (Brent Yorgey) Date: Wed, 23 Sep 2020 05:15:23 -0500 Subject: [Haskell-cafe] Open Kattis Problem Srednji: Hints to improve my algorithm In-Reply-To: <87y2l1arqb.fsf@t450s> References: <87y2l1arqb.fsf@t450s> Message-ID: Viktor is right. Here's a small hint towards one way of solving it: start by replacing every number smaller than B by -1, and every number larger than B by 1 (and B itself by 0). -Brent On Wed, Sep 23, 2020 at 2:17 AM Dominik Bollmann wrote: > > Hi Haskell-Cafe, > > I've been trying to solve the Open Kattis Problem called Srednji > recently, unfortunately without success. > > Given a sequence A and a number B within that sequence this problem asks > to find all odd sub-sequences of A that, when sorted, have B as their > median in the middle. That is, from A we may remove some prefix and/or > suffix and if the resulting sub-sequence -- when sorted -- contains B in > the middle, then this sub-sequence is a solution. The problem asks to > find the number of all solutions. Check out > https://open.kattis.com/problems/srednji for the details. > > My Haskell solution below tries to find the number of odd sub-sequences > by first locating the median and then repeatedly moving left and right > from that median to find larger and larger sub-sequence candidates. Each > found candidate is checked to have B in the middle when sorted in order > to become a solution. Moreover, I also extend each such candidate > further to the left (and to the right, respectively) to determine > whether these leftward or rightward extensions are solutions, too. > > I think with this approach I systematically enumerate all solutions. > Unfortunately, though, this approach is too slow and times out on the > 11th hidden test cases. > > I'd therefore be thankful for hints about different approaches to > solving this problem more efficiently. > > > Thanks! > > Dominik. > > ==================================================================== > > My current, slow Haskell code is this: > > import Data.Maybe > import Data.Sequence (Seq, (<|), (|>)) > import qualified Data.Sequence as Seq > import qualified Data.Vector.Unboxed as Vec > > data SubSeq = SubSeq > { getBalance :: {-# UNPACK #-} !Int > , getSubSeq :: Seq Int > , from :: {-# UNPACK #-} !Int > , to :: {-# UNPACK #-} !Int > } > > balancedSubSeqs :: [Int] -> Int -> [SubSeq] > balancedSubSeqs seq med = do > candidate <- leftRight [val0] > let lefts = leftLeft candidate [] > rights = rightRight candidate [] > candidate ?: lefts ++ rights > where > medidx = fromJust (Vec.findIndex (== med) arr) > val0 = SubSeq 0 (Seq.singleton med) medidx medidx > arr = Vec.fromList seq > > leftRight cands@(SubSeq balance seq i j : _) > | i-1 < 0 || j+1 >= Vec.length arr = cands > | otherwise = > let v1 = arr Vec.! (i-1) > v2 = arr Vec.! (j+1) > balance' = newBalance balance v1 v2 > seq' = (v1 <| seq) |> v2 > in leftRight (SubSeq balance' seq' (i-1) (j+1) : cands) > > leftLeft cand@(SubSeq balance seq i j) sols > | i-2 < 0 = sols > | otherwise = > let v1 = arr Vec.! (i-2) > v2 = arr Vec.! (i-1) > balance' = newBalance balance v1 v2 > seq' = v1 <| v2 <| seq > newCand = SubSeq balance' seq' (i-2) j > in leftLeft newCand (newCand ?: sols) > > rightRight cand@(SubSeq balance seq i j) sols > | j+2 >= Vec.length arr = sols > | otherwise = > let v1 = arr Vec.! (j+1) > v2 = arr Vec.! (j+2) > balance' = newBalance balance v1 v2 > seq' = seq |> v1 |> v2 > newCand = SubSeq balance' seq' i (j+2) > in rightRight newCand (newCand ?: sols) > > newBalance old n1 n2 > | n1 < med, n2 < med = old - 2 > | n1 > med, n2 > med = old + 2 > | otherwise = old > > infixr 5 ?: > --(?:) :: SubSeq -> [SubSeq] -> [SubSeq] > x@(SubSeq b _ _ _) ?: xs > | b == 0 = x : xs > | otherwise = xs > > main :: IO () > main = do > [len, median] <- fmap read . words <$> getLine > seq <- fmap read . words <$> getLine > let solutions = balancedSubSeqs seq median > print (length solutions) > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From ietf-dane at dukhovni.org Thu Sep 24 09:59:18 2020 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Thu, 24 Sep 2020 05:59:18 -0400 Subject: [Haskell-cafe] Open Kattis Problem Srednji: Hints to improve my algorithm In-Reply-To: <20200923075733.GD64864@straasha.imrryr.org> References: <87y2l1arqb.fsf@t450s> <20200923075733.GD64864@straasha.imrryr.org> Message-ID: <20200924095918.GH64864@straasha.imrryr.org> On Wed, Sep 23, 2020 at 03:57:33AM -0400, Viktor Dukhovni wrote: > The best algorithm that comes to mind runs in linear time in the length > of the list, and requires linear (2N) additional space. No sorting > (that would not be linear) or complex testing of candidates is required, > just some counting and O(N) book-keeping. On my machine the constant factor seems to be about 0.7 seconds for a randomly "desorted" list of length 10 million numbers, in which choosing the desired median to be 5 million yields 9,979,641,307 possible combinations of sequences: 9979641307 2,160,167,416 bytes allocated in the heap 106,240 bytes copied during GC 80,053,184 bytes maximum residency (2 sample(s)) 744,512 bytes maximum slop 80 MiB total memory in use (0 MB lost due to fragmentation) Tot time (elapsed) Avg pause Max pause Gen 0 960 colls, 0 par 0.005s 0.005s 0.0000s 0.0001s Gen 1 2 colls, 0 par 0.005s 0.005s 0.0026s 0.0050s INIT time 0.000s ( 0.000s elapsed) MUT time 0.652s ( 0.698s elapsed) GC time 0.010s ( 0.010s elapsed) EXIT time 0.000s ( 0.000s elapsed) Total time 0.663s ( 0.708s elapsed) %GC time 0.0% (0.0% elapsed) Alloc rate 3,311,613,717 bytes per MUT second Productivity 98.4% of total user, 98.5% of total elapsed The tuned up algorithm uses 1*N+constant space, which for 10 million 64-bit Ints in an Unboxed Vector works out to the reported 80 MB. Most of the CPU time (and heap allocaton) is likely spent reading and converting the input stream of decimal integers. The actual CPU time spent solving the problem is likely a fraction of that cost. The RTS stats for 100M numbers confirm the linear scaling in time and space (this time 103,749,385,441 ways to place the median): 103749385441 21,701,903,208 bytes allocated in the heap 935,496 bytes copied during GC 800,053,240 bytes maximum residency (2 sample(s)) 67,592 bytes maximum slop 766 MiB total memory in use (0 MB lost due to fragmentation) Tot time (elapsed) Avg pause Max pause Gen 0 9610 colls, 0 par 0.052s 0.052s 0.0000s 0.0001s Gen 1 2 colls, 0 par 0.049s 0.049s 0.0247s 0.0491s INIT time 0.000s ( 0.000s elapsed) MUT time 6.811s ( 7.297s elapsed) GC time 0.101s ( 0.102s elapsed) EXIT time 0.000s ( 0.000s elapsed) Total time 6.912s ( 7.399s elapsed) %GC time 0.0% (0.0% elapsed) Alloc rate 3,186,237,035 bytes per MUT second Productivity 98.5% of total user, 98.6% of total elapsed -- Viktor. From dominikbollmann at gmail.com Thu Sep 24 19:15:19 2020 From: dominikbollmann at gmail.com (Dominik Bollmann) Date: Thu, 24 Sep 2020 21:15:19 +0200 Subject: [Haskell-cafe] Open Kattis Problem Srednji: Hints to improve my algorithm In-Reply-To: References: <87y2l1arqb.fsf@t450s> Message-ID: <873637ugu0.fsf@t450s> Thank you for the input and hints, Viktor and Brent. I appreciate it! I'll try to come up with a better algorithm. Thanks! Dominik Brent Yorgey writes: > Viktor is right. Here's a small hint towards one way of solving it: start > by replacing every number smaller than B by -1, and every number larger > than B by 1 (and B itself by 0). > > -Brent > > On Wed, Sep 23, 2020 at 2:17 AM Dominik Bollmann > wrote: > >> >> Hi Haskell-Cafe, >> >> I've been trying to solve the Open Kattis Problem called Srednji >> recently, unfortunately without success. >> >> Given a sequence A and a number B within that sequence this problem asks >> to find all odd sub-sequences of A that, when sorted, have B as their >> median in the middle. That is, from A we may remove some prefix and/or >> suffix and if the resulting sub-sequence -- when sorted -- contains B in >> the middle, then this sub-sequence is a solution. The problem asks to >> find the number of all solutions. Check out >> https://open.kattis.com/problems/srednji for the details. >> >> My Haskell solution below tries to find the number of odd sub-sequences >> by first locating the median and then repeatedly moving left and right >> from that median to find larger and larger sub-sequence candidates. Each >> found candidate is checked to have B in the middle when sorted in order >> to become a solution. Moreover, I also extend each such candidate >> further to the left (and to the right, respectively) to determine >> whether these leftward or rightward extensions are solutions, too. >> >> I think with this approach I systematically enumerate all solutions. >> Unfortunately, though, this approach is too slow and times out on the >> 11th hidden test cases. >> >> I'd therefore be thankful for hints about different approaches to >> solving this problem more efficiently. >> >> >> Thanks! >> >> Dominik. >> >> ==================================================================== >> >> My current, slow Haskell code is this: >> >> import Data.Maybe >> import Data.Sequence (Seq, (<|), (|>)) >> import qualified Data.Sequence as Seq >> import qualified Data.Vector.Unboxed as Vec >> >> data SubSeq = SubSeq >> { getBalance :: {-# UNPACK #-} !Int >> , getSubSeq :: Seq Int >> , from :: {-# UNPACK #-} !Int >> , to :: {-# UNPACK #-} !Int >> } >> >> balancedSubSeqs :: [Int] -> Int -> [SubSeq] >> balancedSubSeqs seq med = do >> candidate <- leftRight [val0] >> let lefts = leftLeft candidate [] >> rights = rightRight candidate [] >> candidate ?: lefts ++ rights >> where >> medidx = fromJust (Vec.findIndex (== med) arr) >> val0 = SubSeq 0 (Seq.singleton med) medidx medidx >> arr = Vec.fromList seq >> >> leftRight cands@(SubSeq balance seq i j : _) >> | i-1 < 0 || j+1 >= Vec.length arr = cands >> | otherwise = >> let v1 = arr Vec.! (i-1) >> v2 = arr Vec.! (j+1) >> balance' = newBalance balance v1 v2 >> seq' = (v1 <| seq) |> v2 >> in leftRight (SubSeq balance' seq' (i-1) (j+1) : cands) >> >> leftLeft cand@(SubSeq balance seq i j) sols >> | i-2 < 0 = sols >> | otherwise = >> let v1 = arr Vec.! (i-2) >> v2 = arr Vec.! (i-1) >> balance' = newBalance balance v1 v2 >> seq' = v1 <| v2 <| seq >> newCand = SubSeq balance' seq' (i-2) j >> in leftLeft newCand (newCand ?: sols) >> >> rightRight cand@(SubSeq balance seq i j) sols >> | j+2 >= Vec.length arr = sols >> | otherwise = >> let v1 = arr Vec.! (j+1) >> v2 = arr Vec.! (j+2) >> balance' = newBalance balance v1 v2 >> seq' = seq |> v1 |> v2 >> newCand = SubSeq balance' seq' i (j+2) >> in rightRight newCand (newCand ?: sols) >> >> newBalance old n1 n2 >> | n1 < med, n2 < med = old - 2 >> | n1 > med, n2 > med = old + 2 >> | otherwise = old >> >> infixr 5 ?: >> --(?:) :: SubSeq -> [SubSeq] -> [SubSeq] >> x@(SubSeq b _ _ _) ?: xs >> | b == 0 = x : xs >> | otherwise = xs >> >> main :: IO () >> main = do >> [len, median] <- fmap read . words <$> getLine >> seq <- fmap read . words <$> getLine >> let solutions = balancedSubSeqs seq median >> print (length solutions) >> _______________________________________________ >> Haskell-Cafe mailing list >> To (un)subscribe, modify options or view archives go to: >> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >> Only members subscribed via the mailman list are allowed to post. From ietf-dane at dukhovni.org Thu Sep 24 21:55:18 2020 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Thu, 24 Sep 2020 17:55:18 -0400 Subject: [Haskell-cafe] Open Kattis Problem Srednji: Hints to improve my algorithm In-Reply-To: <873637ugu0.fsf@t450s> References: <87y2l1arqb.fsf@t450s> <873637ugu0.fsf@t450s> Message-ID: <20200924215518.GI64864@straasha.imrryr.org> On Thu, Sep 24, 2020 at 09:15:19PM +0200, Dominik Bollmann wrote: > Thank you for the input and hints, Viktor and Brent. I appreciate it! > I'll try to come up with a better algorithm. Good luck. Indeed once an efficient algorithm is implemented, the bulk of the runtime is doing the I/O and deserialisation of the input values. With `getContents` and `readInt` from Data.ByteString.Lazy.Char8 the runtime for 100 million ints was ~6.8 seconds, while with `stdin` and `readInt` from Data.ByteString.Streaming.stdin + it was ~25s, but a more efficient `readInt` replacement for streaming ByteStrings brings that down to 5.5s. A loop in C using `scanf("%" PRIu64, &n)`, decodes 100M Ints in ~10s on the same machine, which slower than the Haskell code do the same and also solving this exercise, likely due to stdio(3) not being particularly efficient, and scanf(3) having to reparse the format string on every call. In any case, this clearly points in the direction of reading and converting the ASCII decimals as being the dominant cost in this problem. -- Viktor. From leah at vuxu.org Fri Sep 25 11:31:21 2020 From: leah at vuxu.org (Leah Neukirchen) Date: Fri, 25 Sep 2020 13:31:21 +0200 Subject: [Haskell-cafe] Virtual Munich Haskell Meeting, 2020-09-28 @ 19:30 Message-ID: <875z82qeie.fsf@vuxu.org> Dear all, Next week, our monthly Munich Haskell Meeting will take place again on Monday, September 28 at 19:30. **Due to meetup limitations in Bavaria, this meeting will take place online!** For details see here: https://muenchen.haskell.bayern/dates.html Everybody is welcome, especially the Haskellers from Bavaria that do not usually come to our Munich meetings due to travel distance! cu, -- Leah Neukirchen https://leahneukirchen.org/ From delphine.demange at irisa.fr Mon Sep 28 09:10:16 2020 From: delphine.demange at irisa.fr (Delphine Demange) Date: Mon, 28 Sep 2020 11:10:16 +0200 Subject: [Haskell-cafe] Compiler Construction (CC) 2021 - Call for Papers Message-ID: <10DA907E-0BF2-4C3D-89FB-24ADCC0A4B33@irisa.fr> ACM SIGPLAN 2021 International Conference on Compiler Construction (CC 2021) Co-located with CGO, HPCA and PPoPP Sat 27 February - Wed 3 March 2021 https://conf.researchr.org/home/CC-2021 CALL FOR PAPERS The International Conference on Compiler Construction (CC) is interested in work on processing programs in the most general sense: analyzing, transforming or executing input that describes how a system operates, including traditional compiler construction as a special case. = IMPORTANT DATES = Abstract Submission: November 8, 2020 Full Paper Submission: November 10, 2020 Author Response Period: December 7 - 9, 2020 Author Notification: December 22, 2020 Artifact Submission: January 5, 2021 AE Notification: January 20, 2021 Final Papers due: January 22, 2021 Conference: February 27 - March 3, 2021 Original contributions are solicited on the topics of interest which include, but are not limited to: - Compilation and interpretation techniques, including program representation, analysis, and transformation; code generation, optimization, and synthesis; the verification thereof - Run-time techniques, including memory management, virtual machines, and dynamic and just-in-time compilation - Programming tools, including refactoring editors, checkers, verifiers, compilers, debuggers, and profilers - Techniques, ranging from programming languages to micro-architectural support, for specific domains such as secure, parallel, distributed, embedded or mobile environments - Design and implementation of novel language constructs, programming models, and domain-specific languages CC is an ACM SIGPLAN conference, and implements guidelines and procedures recommended by SIGPLAN (https://www.sigplan.org). Prospective authors should be aware of SIGPLAN’s Copyright policies. Proceedings will be made available online in the ACM digital library from one week before to one week after the conference. Full CfP: https://conf.researchr.org/track/CC-2021/cc-research-papers ARTIFACT EVALUATION Authors of accepted papers will be invited to submit their artifacts for the Artifact Evaluation (AE). The Artifact Evaluation process begins after the acceptance notification, and is run by a separate committee whose task is to assess how the artifacts support the work described in the papers. To ease the organization of the AE committee, we kindly ask authors to indicate at the time they submit the paper, whether they are interested in submitting an artifact. Papers that go through the Artifact Evaluation process successfully will receive a seal of approval printed on the papers themselves. Authors of accepted papers are encouraged, but not required, to make these materials publicly available upon publication of the proceedings, by including them as “source materials” in the ACM Digital Library. CC AE web page: https://conf.researchr.org/track/CC-2021/research-artifacts ORGANIZERS General Chair: Aaron Smith - Microsoft / University of Edinburgh Program Chairs: Delphine Demange - Univ Rennes, Inria, CNRS, IRISA Rajiv Gupta - UC Riverside Artifact Evaluation Chairs: Bruno Bodin - Yale-NUS College Michel Steuwer - University of Glasgow Web Chair: Martin Lücke - University of Edinburgh Steering Committee: Björn Franke (Chair) - University of Edinburgh Jose Nelson Amaral - University of Alberta Christophe Dubach - University of Edinburgh Sebastian Hack - Saarland University Manuel Hermenegildo - IMDEA Software Institute and T.U. of Madrid (UPM) Alexandra Jimborean - University of Murcia Milind Kulkarni - Purdue University Louis-Noël Pouchet - Colorado State University Peng Wu - Futurewei Technologies Jingling Xue - UNSW Sydney Ayal Zaks - Intel Corporation and Technion Program Committee: Guillaume Baudart - IBM Research Walter Binder - University of Lugano Simone Campanoni - Northwestern University Albert Cohen - Google Caroline Collange - Inria, Univ Rennes, CNRS, IRISA Huimin Cui - Institute of Computing Technology, CAS Christophe Dubach - McGill University Benoît Dupont de Dinechin - Kalray Bernhard Egger - Seoul National University Christine Flood - Red Hat Laure Gonnord - University of Lyon and LIP Myoungsoo Jung - KAIST Andrew Kennedy - Facebook Dongyoon Lee - Stony Brook University Christian Lengauer - University of Passau Xavier Leroy - Collège de France and Inria Yun Liang - Peking University Toby Murray - University of Melbourne and Data61 Biswabandan Panda - Indian Institute of Technology Kanpur Santosh Pande - Georgia Tech Louis-Noël Pouchet - Colorado State University Gabriel Rodríguez - Universidade da Coruña Jan Vitek - Northeastern University Jingling Xue - UNSW Sydney Zhijia Zhao - UC Riverside From dj112358 at outlook.com Mon Sep 28 16:37:08 2020 From: dj112358 at outlook.com (David James) Date: Mon, 28 Sep 2020 16:37:08 +0000 Subject: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks Message-ID: Hello – I’m proposing to restructure this, and I have a draft here. I’ve given my main reasons for the restructuring at the top of the draft page. I’d very much like feedback before updating the real page, especially if people don’t like the new one much. I’m new here, so should probably say a bit about myself. I’ve been learning-by-doing Haskell for about two years. Sometimes I feel I’m starting to get it, but these feelings don’t usually last long. I’m certainly not an expert, and don’t have a PhD in Very Clever Things. I do have quite an extensive IT background, originally programming in Lisp and Prolog, then (sadly) C++, VB and SQL but have had quite a long break from programming (doing dumb things like architecting systems and project managing) before looking at Haskell. I built this website and this library, just for my own amusement. Apologies if this isn’t the right place to send this. (But then where is?) I’ve already put a note on the page itself about the new draft, but I’ve no idea whether anyone will notice it. Thanks very much, David. -------------- next part -------------- An HTML attachment was scrubbed... URL: From b at chreekat.net Mon Sep 28 17:20:45 2020 From: b at chreekat.net (Bryan Richter) Date: Mon, 28 Sep 2020 20:20:45 +0300 Subject: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks In-Reply-To: References: Message-ID: This looks great! Although I admit I didn't read the version you want to replace. :) I'm always in favor of motivated people making good faith changes to (any) wiki. On Monday, September 28, 2020, David James wrote: > Hello – I’m proposing to restructure this, and I have a draft here. I’ve given my main reasons for the restructuring at the top of the draft page. > > > > I’d very much like feedback before updating the real page, especially if people don’t like the new one much. > > > > I’m new here, so should probably say a bit about myself. I’ve been learning-by-doing Haskell for about two years. Sometimes I feel I’m starting to get it, but these feelings don’t usually last long. I’m certainly not an expert, and don’t have a PhD in Very Clever Things. I do have quite an extensive IT background, originally programming in Lisp and Prolog, then (sadly) C++, VB and SQL but have had quite a long break from programming (doing dumb things like architecting systems and project managing) before looking at Haskell. I built this website and this library, just for my own amusement. > > > > Apologies if this isn’t the right place to send this. (But then where is?) I’ve already put a note on the page itself about the new draft, but I’ve no idea whether anyone will notice it. > > > > Thanks very much, > > David. > > > > > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From b at chreekat.net Mon Sep 28 20:47:50 2020 From: b at chreekat.net (Bryan Richter) Date: Mon, 28 Sep 2020 23:47:50 +0300 Subject: [Haskell-cafe] Status of Haskell on the browser? Message-ID: A perennial question, I'm sure. I've been trying out some browser-side programming lately, and I'm curious if anyone in the community has any vision for where things are going. GHCJS looks like it hasn't had any updates in a while. Is it approaching its twilight years, or do people still have plans for it? Asterius looks interesting, but I'm not too clear on where WASM sits in the web space. Would a suitably-advanced Asterius cover all of our Haskell-on-the-browser needs, or would there still be a need for a separate javascript backend? Basically, I'm curious to know what people are working on in this realm! -Bryan -------------- next part -------------- An HTML attachment was scrubbed... URL: From hilco.wijbenga at gmail.com Tue Sep 29 00:11:46 2020 From: hilco.wijbenga at gmail.com (Hilco Wijbenga) Date: Mon, 28 Sep 2020 17:11:46 -0700 Subject: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks In-Reply-To: References: Message-ID: I like it! P.S. I did notice a small typo: mondayS :: State TurnstileState [TurstileOutput] (note the missing 'n'). On Mon, Sep 28, 2020 at 9:38 AM David James wrote: > > Hello – I’m proposing to restructure this, and I have a draft here. I’ve given my main reasons for the restructuring at the top of the draft page. > > > > I’d very much like feedback before updating the real page, especially if people don’t like the new one much. > > > > I’m new here, so should probably say a bit about myself. I’ve been learning-by-doing Haskell for about two years. Sometimes I feel I’m starting to get it, but these feelings don’t usually last long. I’m certainly not an expert, and don’t have a PhD in Very Clever Things. I do have quite an extensive IT background, originally programming in Lisp and Prolog, then (sadly) C++, VB and SQL but have had quite a long break from programming (doing dumb things like architecting systems and project managing) before looking at Haskell. I built this website and this library, just for my own amusement. > > > > Apologies if this isn’t the right place to send this. (But then where is?) I’ve already put a note on the page itself about the new draft, but I’ve no idea whether anyone will notice it. > > > > Thanks very much, > > David. > > > > > > > > > > > > > > > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. From mlang at delysid.org Tue Sep 29 08:27:19 2020 From: mlang at delysid.org (Mario Lang) Date: Tue, 29 Sep 2020 10:27:19 +0200 Subject: [Haskell-cafe] I think I discovered my first Monoid instance Message-ID: <87a6x99ee0.fsf@blind.guru> Hi. A common theme of some Haskell talks seems to be to motivate people to go and hunt for instances of standard typeclasses. As the hobbyist Haskell programmer that I am, I kept on wondering if I would ever find some. It feels sort of unrealistic, or at least unlikely, that I should suddenly start to see meaningful structure in code just because I think I know some typeclasses. I think I finally discovered a Monoid instance that helps to express a concept in code pretty well. However, unsure about the soundness of my blundering about, I'd like to confirm (or disprove!) my findings before I fall in love with the approach too much. In my chess library (chessIO) I use bitboards to represent chess positions. data QuadBitboard = QBB { black :: {-# UNPACK #-} !Word64 , pbq :: {-# UNPACK #-} !Word64 , nbk :: {-# UNPACK #-} !Word64 , rqk :: {-# UNPACK #-} !Word64 } deriving (Eq) A quad bitboard is a space optimisation based on the observation that a square can only be occupied by one piece. occupied QBB{pbq, nbk, rqk} = pbq .|. nbk .|. rqk pnr QBB{pbq, nbk, rqk} = pbq `xor` nbk `xor` rqk white = liftA2 xor occupied black pawns = liftA2 (.&.) pnr pbq knights = liftA2 (.&.) pnr nbk bishops = liftA2 (.&.) pbq nbk rooks = liftA2 (.&.) pnr rqk queens = liftA2 (.&.) pbq rqk kings = liftA2 (.&.) nbk rqk We can create a bitboard with a single occupied square: square :: Int -> Word4 -> QuadBitboard square !sq !nb = QBB (f 0) (f 1) (f 2) (f 3) where !b = bit sq f !n = fromIntegral ((nb `unsafeShiftR` n) .&. 1) * b And, as an aside, we can even use pattern synonyms to give these nibbles meaningful names. pattern NoPiece = 0 pattern WhitePawn = 2 pattern WhiteKnight = 4 pattern WhiteBishop = 6 pattern WhiteRook = 8 pattern WhiteQueen = 10 pattern WhiteKing = 12 pattern BlackPawn = 3 pattern BlackKnight = 5 pattern BlackBishop = 7 pattern BlackRook = 9 pattern BlackQueen = 11 pattern BlackKing = 13 But what felt really like a cool discovery, is combination: instance Monoid QuadBitboard where mempty = QBB 0 0 0 0 -- | bitwise XOR instance Semigroup QuadBitboard where QBB b0 b1 b2 b3 <> QBB b0' b1' b2' b3' = QBB (b0 `xor` b0') (b1 `xor` b1') (b2 `xor` b2') (b3 `xor` b3') With this, we can pretty easily define a function to create a quad bitboard from a string: instance IsString QuadBitboard where fromString = go (7, 0) mempty where go _ !qbb "" = qbb go (!r,_) qbb ('/':xs) = go (r - 1, 0) qbb xs go (!r,!f) !qbb (x:xs) | inRange ('1','8') x = go (r, f + (ord x - ord '0')) qbb xs | otherwise = go (r, f + 1) (qbb <> square (r*8+f) nb) xs where nb = case x of 'P' -> WhitePawn 'N' -> WhiteKnight 'B' -> WhiteBishop 'R' -> WhiteRook 'Q' -> WhiteQueen 'K' -> WhiteKing 'p' -> BlackPawn 'n' -> BlackKnight 'b' -> BlackBishop 'r' -> BlackRook 'q' -> BlackQueen 'k' -> BlackKing _ -> error $ "QuadBitBoard.fromString: Illegal FEN character " <> show x standard :: QuadBitboard standard = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR" The rest of the module builds on the Monoid instance of QuadBitboard. Again, this is the first time a instance like this turns up in my Haskell experiments. It feels extremely satisfying having discovered this. But maybe I am violating some laws (I hear instances do that sometimes!) or some other thing is totally wrong. One might say I am not fully trusting the peace. https://hackage.haskell.org/package/chessIO/docs/Game-Chess-QuadBitboard.html -- CYa, ⡍⠁⠗⠊⠕ From ben.franksen at online.de Tue Sep 29 09:33:58 2020 From: ben.franksen at online.de (Ben Franksen) Date: Tue, 29 Sep 2020 11:33:58 +0200 Subject: [Haskell-cafe] I think I discovered my first Monoid instance In-Reply-To: <87a6x99ee0.fsf@blind.guru> References: <87a6x99ee0.fsf@blind.guru> Message-ID: Am 29.09.20 um 10:27 schrieb Mario Lang: > instance Monoid QuadBitboard where > mempty = QBB 0 0 0 0 > > -- | bitwise XOR > instance Semigroup QuadBitboard where > QBB b0 b1 b2 b3 <> QBB b0' b1' b2' b3' = > QBB (b0 `xor` b0') (b1 `xor` b1') (b2 `xor` b2') (b3 `xor` b3') > > But maybe I am violating some laws The Semigroup is okay, since 'xor' is indeed associative, and your instance basically lifts it to 4-tuples. The Monoid instance is wrong, though. There is no unit for 'xor'! Cheers Ben From lemming at henning-thielemann.de Tue Sep 29 09:38:47 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Tue, 29 Sep 2020 11:38:47 +0200 (CEST) Subject: [Haskell-cafe] I think I discovered my first Monoid instance In-Reply-To: References: <87a6x99ee0.fsf@blind.guru> Message-ID: On Tue, 29 Sep 2020, Ben Franksen wrote: > Am 29.09.20 um 10:27 schrieb Mario Lang: >> instance Monoid QuadBitboard where >> mempty = QBB 0 0 0 0 >> >> -- | bitwise XOR >> instance Semigroup QuadBitboard where >> QBB b0 b1 b2 b3 <> QBB b0' b1' b2' b3' = >> QBB (b0 `xor` b0') (b1 `xor` b1') (b2 `xor` b2') (b3 `xor` b3') >> >> But maybe I am violating some laws > > The Semigroup is okay, since 'xor' is indeed associative, and your > instance basically lifts it to 4-tuples. > > The Monoid instance is wrong, though. There is no unit for 'xor'! Why should 0 not be the identity element? From Andrew.Butterfield at scss.tcd.ie Tue Sep 29 09:41:08 2020 From: Andrew.Butterfield at scss.tcd.ie (Andrew Butterfield) Date: Tue, 29 Sep 2020 10:41:08 +0100 Subject: [Haskell-cafe] I think I discovered my first Monoid instance In-Reply-To: References: <87a6x99ee0.fsf@blind.guru> Message-ID: Hmmm - I thought so at first A B A `xor` B 0 0 0 0 1 1 1 0 1 1 1 0 0 `xor` b = b a `xor` 0 = a Looks like 0 is the identity to me. Regards, Andrew > On 29 Sep 2020, at 10:33, Ben Franksen wrote: > > Am 29.09.20 um 10:27 schrieb Mario Lang: >> instance Monoid QuadBitboard where >> mempty = QBB 0 0 0 0 >> >> -- | bitwise XOR >> instance Semigroup QuadBitboard where >> QBB b0 b1 b2 b3 <> QBB b0' b1' b2' b3' = >> QBB (b0 `xor` b0') (b1 `xor` b1') (b2 `xor` b2') (b3 `xor` b3') >> >> But maybe I am violating some laws > > The Semigroup is okay, since 'xor' is indeed associative, and your > instance basically lifts it to 4-tuples. > > The Monoid instance is wrong, though. There is no unit for 'xor'! > > Cheers > Ben > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------------------------------------------------------------- Andrew Butterfield Tel: +353-1-896-2517 Fax: +353-1-677-2204 Lero at TCD, Head of Software Foundations & Verification Research Group School of Computer Science and Statistics, Room G.39, O'Reilly Institute, Trinity College, University of Dublin http://www.scss.tcd.ie/Andrew.Butterfield/ -------------------------------------------------------------------- -------------- next part -------------- An HTML attachment was scrubbed... URL: From Andrew.Butterfield at scss.tcd.ie Tue Sep 29 09:45:35 2020 From: Andrew.Butterfield at scss.tcd.ie (Andrew Butterfield) Date: Tue, 29 Sep 2020 10:45:35 +0100 Subject: [Haskell-cafe] I think I discovered my first Monoid instance In-Reply-To: References: <87a6x99ee0.fsf@blind.guru> Message-ID: Not only that, `xor` has inverses so it forms an abelian group Regards, Andrew > On 29 Sep 2020, at 10:41, Andrew Butterfield wrote: > > Hmmm - I thought so at first > > A B A `xor` B > 0 0 0 > 0 1 1 > 1 0 1 > 1 1 0 > > 0 `xor` b = b > a `xor` 0 = a > > Looks like 0 is the identity to me. > > Regards, Andrew > >> On 29 Sep 2020, at 10:33, Ben Franksen > wrote: >> >> Am 29.09.20 um 10:27 schrieb Mario Lang: >>> instance Monoid QuadBitboard where >>> mempty = QBB 0 0 0 0 >>> >>> -- | bitwise XOR >>> instance Semigroup QuadBitboard where >>> QBB b0 b1 b2 b3 <> QBB b0' b1' b2' b3' = >>> QBB (b0 `xor` b0') (b1 `xor` b1') (b2 `xor` b2') (b3 `xor` b3') >>> >>> But maybe I am violating some laws >> >> The Semigroup is okay, since 'xor' is indeed associative, and your >> instance basically lifts it to 4-tuples. >> >> The Monoid instance is wrong, though. There is no unit for 'xor'! >> >> Cheers >> Ben >> >> _______________________________________________ >> Haskell-Cafe mailing list >> To (un)subscribe, modify options or view archives go to: >> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >> Only members subscribed via the mailman list are allowed to post. > > -------------------------------------------------------------------- > Andrew Butterfield Tel: +353-1-896-2517 Fax: +353-1-677-2204 > Lero at TCD, Head of Software Foundations & Verification Research Group > School of Computer Science and Statistics, > Room G.39, O'Reilly Institute, Trinity College, University of Dublin > http://www.scss.tcd.ie/Andrew.Butterfield/ > -------------------------------------------------------------------- > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------------------------------------------------------------- Andrew Butterfield Tel: +353-1-896-2517 Fax: +353-1-677-2204 Lero at TCD, Head of Software Foundations & Verification Research Group School of Computer Science and Statistics, Room G.39, O'Reilly Institute, Trinity College, University of Dublin http://www.scss.tcd.ie/Andrew.Butterfield/ -------------------------------------------------------------------- -------------- next part -------------- An HTML attachment was scrubbed... URL: From ben.franksen at online.de Tue Sep 29 09:49:24 2020 From: ben.franksen at online.de (Ben Franksen) Date: Tue, 29 Sep 2020 11:49:24 +0200 Subject: [Haskell-cafe] I think I discovered my first Monoid instance In-Reply-To: References: <87a6x99ee0.fsf@blind.guru> Message-ID: Am 29.09.20 um 11:38 schrieb Henning Thielemann: > > On Tue, 29 Sep 2020, Ben Franksen wrote: > >> Am 29.09.20 um 10:27 schrieb Mario Lang: >>> instance Monoid QuadBitboard where >>>   mempty = QBB 0 0 0 0 >>> >>> -- | bitwise XOR >>> instance Semigroup QuadBitboard where >>>   QBB b0 b1 b2 b3 <> QBB b0' b1' b2' b3' = >>>     QBB (b0 `xor` b0') (b1 `xor` b1') (b2 `xor` b2') (b3 `xor` b3') >>> >>> But maybe I am violating some laws >> >> The Semigroup is okay, since 'xor' is indeed associative, and your >> instance basically lifts it to 4-tuples. >> >> The Monoid instance is wrong, though. There is no unit for 'xor'! > > Why should 0 not be the identity element? Sorry, i was confused. Indeed 0 is a unit. The Monoid instance is lawful. Cheers Ben From lemming at henning-thielemann.de Tue Sep 29 09:50:53 2020 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Tue, 29 Sep 2020 11:50:53 +0200 (CEST) Subject: [Haskell-cafe] I think I discovered my first Monoid instance In-Reply-To: References: <87a6x99ee0.fsf@blind.guru> Message-ID: On Tue, 29 Sep 2020, Andrew Butterfield wrote: > Not only that, `xor` has inverses so it forms an abelian group ... and it's no surprise since 'xor' is '+' in ℤ₂. From oleg.grenrus at iki.fi Tue Sep 29 09:51:23 2020 From: oleg.grenrus at iki.fi (Oleg Grenrus) Date: Tue, 29 Sep 2020 12:51:23 +0300 Subject: [Haskell-cafe] I think I discovered my first Monoid instance In-Reply-To: References: <87a6x99ee0.fsf@blind.guru> Message-ID: <237b8368-8b65-fcad-3b4d-b12e060cc67c@iki.fi> xor a b = (a + b) mod 2 Many good properties follow. https://en.wikipedia.org/wiki/GF(2) ... - Oleg On 29.9.2020 12.45, Andrew Butterfield wrote: > Not only that, `xor` has inverses so it forms an abelian group > > Regards, Andrew > >> On 29 Sep 2020, at 10:41, Andrew Butterfield >> > > wrote: >> >> Hmmm - I thought so at first >> >> A B A `xor` B >> 0 0      0 >> 0 1      1 >> 1 0      1 >> 1 1      0 >> >> 0 `xor` b = b >> a `xor` 0 = a >> >> Looks like 0 is the identity to me. >> >> Regards, Andrew >> >>> On 29 Sep 2020, at 10:33, Ben Franksen >> > wrote: >>> >>> Am 29.09.20 um 10:27 schrieb Mario Lang: >>>> instance Monoid QuadBitboard where >>>>  mempty = QBB 0 0 0 0 >>>> >>>> -- | bitwise XOR >>>> instance Semigroup QuadBitboard where >>>>  QBB b0 b1 b2 b3 <> QBB b0' b1' b2' b3' = >>>>    QBB (b0 `xor` b0') (b1 `xor` b1') (b2 `xor` b2') (b3 `xor` b3') >>>> >>>> But maybe I am violating some laws >>> >>> The Semigroup is okay, since 'xor' is indeed associative, and your >>> instance basically lifts it to 4-tuples. >>> >>> The Monoid instance is wrong, though. There is no unit for 'xor'! >>> >>> Cheers >>> Ben >>> >>> _______________________________________________ >>> Haskell-Cafe mailing list >>> To (un)subscribe, modify options or view archives go to: >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >>> Only members subscribed via the mailman list are allowed to post. >> >> -------------------------------------------------------------------- >> Andrew Butterfield     Tel: +353-1-896-2517     Fax: +353-1-677-2204 >> Lero at TCD, Head of Software Foundations & Verification Research Group >> School of Computer Science and Statistics, >> Room G.39, O'Reilly Institute, Trinity College, University of Dublin >>                          http://www.scss.tcd.ie/Andrew.Butterfield/ >> -------------------------------------------------------------------- >> >> _______________________________________________ >> Haskell-Cafe mailing list >> To (un)subscribe, modify options or view archives go to: >> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe >> Only members subscribed via the mailman list are allowed to post. > > -------------------------------------------------------------------- > Andrew Butterfield     Tel: +353-1-896-2517     Fax: +353-1-677-2204 > Lero at TCD, Head of Software Foundations & Verification Research Group > School of Computer Science and Statistics, > Room G.39, O'Reilly Institute, Trinity College, University of Dublin >                          http://www.scss.tcd.ie/Andrew.Butterfield/ > -------------------------------------------------------------------- > > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From jaro.reinders at gmail.com Tue Sep 29 09:52:38 2020 From: jaro.reinders at gmail.com (Jaro Reinders) Date: Tue, 29 Sep 2020 11:52:38 +0200 Subject: [Haskell-cafe] I think I discovered my first Monoid instance In-Reply-To: References: <87a6x99ee0.fsf@blind.guru> Message-ID: <29a56e1c-b1a9-1660-d4ed-8570d6ccc8f8@gmail.com> But we're talking about xor for the Word64 type, so it is slightly more complicated to prove that it has a unit. From Andrew.Butterfield at scss.tcd.ie Tue Sep 29 09:58:12 2020 From: Andrew.Butterfield at scss.tcd.ie (Andrew Butterfield) Date: Tue, 29 Sep 2020 10:58:12 +0100 Subject: [Haskell-cafe] I think I discovered my first Monoid instance In-Reply-To: <29a56e1c-b1a9-1660-d4ed-8570d6ccc8f8@gmail.com> References: <87a6x99ee0.fsf@blind.guru> <29a56e1c-b1a9-1660-d4ed-8570d6ccc8f8@gmail.com> Message-ID: <72E665EE-3AE6-43C4-8509-8394E79D002D@scss.tcd.ie> It's bitwise, so not that complicated > On 29 Sep 2020, at 10:52, Jaro Reinders wrote: > > But we're talking about xor for the Word64 type, so it is slightly more > complicated to prove that it has a unit. > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------------------------------------------------------------- Andrew Butterfield Tel: +353-1-896-2517 Fax: +353-1-677-2204 Lero at TCD, Head of Software Foundations & Verification Research Group School of Computer Science and Statistics, Room G.39, O'Reilly Institute, Trinity College, University of Dublin http://www.scss.tcd.ie/Andrew.Butterfield/ -------------------------------------------------------------------- -------------- next part -------------- An HTML attachment was scrubbed... URL: From branimir.maksimovic at gmail.com Tue Sep 29 12:16:31 2020 From: branimir.maksimovic at gmail.com (Branimir Maksimovic) Date: Tue, 29 Sep 2020 14:16:31 +0200 Subject: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks In-Reply-To: References: Message-ID: <80de4375-cf5a-8d2a-421b-cc23d5002fd2@gmail.com> Two years is not enough to do anything seroius in Haskell. That is beyond demo programs. Greets, Branimir. On 9/28/20 6:37 PM, David James wrote: > > Hello – I’m proposing to restructure this > , > and I have a draft here > . I’ve given > my main reasons for the restructuring at the top of the draft page. > > I’d very much like feedback before updating the real page, especially > if people don’t like the new one much. > > I’m new here, so should probably say a bit about myself. I’ve been > learning-by-doing Haskell for about two years. Sometimes I feel I’m > starting to get it, but these feelings don’t usually last long. I’m > certainly not an expert, and don’t have a PhD in Very Clever Things. I > do have quite an extensive IT background, originally programming in > Lisp and Prolog, then (sadly) C++, VB and SQL but have had quite a > long break from programming (doing dumb things like architecting > systems and project managing) before looking at Haskell. I built this > website and this library > , just for my own amusement. > > Apologies if this isn’t the right place to send this. (But then where > is?) I’ve already put a note on the page itself about the new draft, > but I’ve no idea whether anyone will notice it. > > Thanks very much, > > David. > > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From guthrie at miu.edu Tue Sep 29 13:10:12 2020 From: guthrie at miu.edu (Gregory Guthrie) Date: Tue, 29 Sep 2020 13:10:12 +0000 Subject: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks In-Reply-To: <80de4375-cf5a-8d2a-421b-cc23d5002fd2@gmail.com> References: <80de4375-cf5a-8d2a-421b-cc23d5002fd2@gmail.com> Message-ID: If this is true, Haskell will probably never move up into even the top 40 of used languages, which would be too bad! And it would be interesting to see why - certainly people find good productivity in the mainstream IP languages in even 6-9 months. (Look at salaries from bootcamp placements.) Currently Haskell is significantly below Cobol, Fortran, Lisp, and Pascal in the language usage/ratings. :-( Dr. Gregory Guthrie Maharishi International University ---------------------------------------------------------------- From: Haskell-Cafe On Behalf Of Branimir Maksimovic Sent: Tuesday, September 29, 2020 7:17 AM To: haskell-cafe at haskell.org Subject: Re: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks Two years is not enough to do anything seroius in Haskell. That is beyond demo programs. Greets, Branimir. On 9/28/20 6:37 PM, David James wrote: Hello - I'm proposing to restructure this, and I have a draft here. I've given my main reasons for the restructuring at the top of the draft page. I'd very much like feedback before updating the real page, especially if people don't like the new one much. I'm new here, so should probably say a bit about myself. I've been learning-by-doing Haskell for about two years. Sometimes I feel I'm starting to get it, but these feelings don't usually last long. I'm certainly not an expert, and don't have a PhD in Very Clever Things. I do have quite an extensive IT background, originally programming in Lisp and Prolog, then (sadly) C++, VB and SQL but have had quite a long break from programming (doing dumb things like architecting systems and project managing) before looking at Haskell. I built this website and this library, just for my own amusement. Apologies if this isn't the right place to send this. (But then where is?) I've already put a note on the page itself about the new draft, but I've no idea whether anyone will notice it. Thanks very much, David. _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From jaro.reinders at gmail.com Tue Sep 29 13:37:17 2020 From: jaro.reinders at gmail.com (Jaro Reinders) Date: Tue, 29 Sep 2020 15:37:17 +0200 Subject: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks In-Reply-To: References: <80de4375-cf5a-8d2a-421b-cc23d5002fd2@gmail.com> Message-ID: <4a813a32-842f-f744-c515-f35e77c55096@gmail.com> I think part of the reason is that most mainstream languages are very similar (imperative, strict and impure) and Haskell does many things radically different. I also think that focused study can bring down the time to become productive significantly. I have personally started learning Haskell by doing hobby projects when I had the time. Studying and using Haskell full-time would certainly have sped up my learning progress. I would also like to point out that language usage statistics are not reliable. The TIOBE index, based on number of search results by popular search engines, indeed puts Haskell below the languages that you mention, but for example the RedMonk rankings put Haskell above those languages, both by the number of Stack Overflow questions and by the number of GitHub projects. The PYPL PopularitY ranking, based on the number of Google searches for language tutorials, puts only Cobol above Haskell (the other languages are not listed). On 9/29/20 3:10 PM, Gregory Guthrie wrote: > If this is true, Haskell will probably never move up into even the top 40 of > used languages, which would be too bad! And it would be interesting to see why > – certainly people find good productivity in the mainstream IP languages in > even 6-9 months. > >   (Look at salaries from bootcamp placements.) > >   > > Currently Haskell is significantly below Cobol, Fortran, Lisp, and Pascal in > the language usage/ratings.   :-( > >   > >   > > Dr. Gregory Guthrie > > Maharishi International University > > ---------------------------------------------------------------- > >   > > *From:* Haskell-Cafe *On Behalf Of *Branimir > Maksimovic > *Sent:* Tuesday, September 29, 2020 7:17 AM > *To:* haskell-cafe at haskell.org > *Subject:* Re: [Haskell-cafe] Proposed "Restructuring" of State Monad page in > Haskell Wikibooks > >   > > Two years is not enough to do anything seroius in Haskell. That is beyond demo > programs. > >   > > Greets, Branimir. > >   > > On 9/28/20 6:37 PM, David James wrote: > > Hello – I’m proposing to restructure this > , and I > have a draft here > . I’ve given my > main reasons for the restructuring at the top of the draft page. > >   > > I’d very much like feedback before updating the real page, especially if > people don’t like the new one much. > >   > > I’m new here, so should probably say a bit about myself. I’ve been > learning-by-doing Haskell for about two years. Sometimes I feel I’m > starting to get it, but these feelings don’t usually last long. I’m > certainly not an expert, and don’t have a PhD in Very Clever Things. I do > have quite an extensive IT background, originally programming in Lisp and > Prolog, then (sadly) C++, VB and SQL but have had quite a long break from > programming (doing dumb things like architecting systems and project > managing) before looking at Haskell. I built this website > and this library > , just for my own amusement. > >   > > Apologies if this isn’t the right place to send this. (But then where is?) > I’ve already put a note on the page itself about the new draft, but I’ve no > idea whether anyone will notice it. > >   > > Thanks very much, > > David. > >   > >   > >   > >   > >   > >   > >   > > > > _______________________________________________ > > Haskell-Cafe mailing list > > To (un)subscribe, modify options or view archives go to: > > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > > Only members subscribed via the mailman list are allowed to post. > > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. > From tom.smeding at gmail.com Tue Sep 29 13:44:42 2020 From: tom.smeding at gmail.com (Tom Smeding) Date: Tue, 29 Sep 2020 15:44:42 +0200 Subject: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks In-Reply-To: References: <80de4375-cf5a-8d2a-421b-cc23d5002fd2@gmail.com> Message-ID: Hi all, Branimir wrote: > Two years is not enough to do anything serious in Haskell. That is beyond demo programs. This statement carries a lot of assumptions, some of which may be true in this case, some of which may not -- I don't know David James -- but I think it isn't good to let this statement stand on its own like that. If a student who has never programmed before starts learning Haskell, they will not be able to succeed professionally after just two years unless they have other useful background knowledge and/or talent (let's not discuss whether talent is a thing or not). However, the email by David James did not sound like he is a beginner in the act of programming; indeed, with experience in imperative (C++, VB), declarative (SQL), functional (Lisp) and logic (Prolog) programming languages, that certainly does not count as "has never programmed before". And I think we can agree that someone with sufficient experience in programming at large can learn to use Haskell effectively in two years (which is quite a long time, even if it's irregular practice). At least, I strongly believe this until proven otherwise. If that is indeed not the case, then something is seriously wrong on Haskell's side, as Gregory has noted below. David: I haven't read your rewritten State monad tutorial, for which my apologies; also apologies for kind of derailing this thread. Cheers, and have a great day, Tom Smeding P.S. This message wasn't accepted by the mailing list earlier, so was just sent to some people personally. Sorry for duplicate emails. On Tue, 29 Sep 2020 at 15:14, Gregory Guthrie wrote: > If this is true, Haskell will probably never move up into even the top 40 > of used languages, which would be too bad! And it would be interesting to > see why – certainly people find good productivity in the mainstream IP > languages in even 6-9 months. > > (Look at salaries from bootcamp placements.) > > > > Currently Haskell is significantly below Cobol, Fortran, Lisp, and Pascal > in the language usage/ratings. :-( > > > > > > Dr. Gregory Guthrie > > Maharishi International University > > ---------------------------------------------------------------- > > > > *From:* Haskell-Cafe *On Behalf Of *Branimir > Maksimovic > *Sent:* Tuesday, September 29, 2020 7:17 AM > *To:* haskell-cafe at haskell.org > *Subject:* Re: [Haskell-cafe] Proposed "Restructuring" of State Monad > page in Haskell Wikibooks > > > > Two years is not enough to do anything seroius in Haskell. That is beyond > demo programs. > > > > Greets, Branimir. > > > > On 9/28/20 6:37 PM, David James wrote: > > Hello – I’m proposing to restructure this > , and I > have a draft here > . I’ve given my > main reasons for the restructuring at the top of the draft page. > > > > I’d very much like feedback before updating the real page, especially if > people don’t like the new one much. > > > > I’m new here, so should probably say a bit about myself. I’ve been > learning-by-doing Haskell for about two years. Sometimes I feel I’m > starting to get it, but these feelings don’t usually last long. I’m > certainly not an expert, and don’t have a PhD in Very Clever Things. I do > have quite an extensive IT background, originally programming in Lisp and > Prolog, then (sadly) C++, VB and SQL but have had quite a long break from > programming (doing dumb things like architecting systems and project > managing) before looking at Haskell. I built this website > and this library > , just for my own amusement. > > > > Apologies if this isn’t the right place to send this. (But then where is?) > I’ve already put a note on the page itself about the new draft, but I’ve no > idea whether anyone will notice it. > > > > Thanks very much, > > David. > > > > > > > > > > > > > > > > > > _______________________________________________ > > Haskell-Cafe mailing list > > To (un)subscribe, modify options or view archives go to: > > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > > Only members subscribed via the mailman list are allowed to post. > > _______________________________________________ > Haskell-Cafe mailing list > To (un)subscribe, modify options or view archives go to: > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe > Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From P.Achten at cs.ru.nl Tue Sep 29 13:46:18 2020 From: P.Achten at cs.ru.nl (Peter Achten) Date: Tue, 29 Sep 2020 15:46:18 +0200 Subject: [Haskell-cafe] [TFP'21] first call for papers: Trends in Functional Programming 2021, 17-19 February (with Lambda Days 2021 & TFPIE 2021) Message-ID: <1ee8410b-f96b-b119-b3df-8dfecfe4c167@cs.ru.nl> -------------------------------------------------------------------------                      First call for papers         22nd Symposium on Trends in Functional Programming                           tfp2021.org ------------------------------------------------------------------------- The symposium on Trends in Functional Programming (TFP) is an international forum for researchers with interests in all aspects of functional programming, taking a broad view of current and future trends in the area. It aspires to be a lively environment for presenting the latest research results, and other contributions. * TFP offers a supportive reviewing process designed to help less experienced   authors succeed, with two rounds of review, both before and after the   symposium itself. Authors have an opportunity to address reviewers' concerns   before final decisions on publication in the proceedings. * TFP offers two "best paper" awards, the John McCarthy award for best paper,   and the David Turner award for best student paper. * TFP is co-located with Lambda Days in beautiful Krakow. Lambda Days is a   vibrant developer conference with hundreds of attendees and a lively programme   of talks on functional programming in practice. Due to the covid pandemic,   the event is online with a lot of attention to interaction and getting to   socialize with the community. Important Dates --------------- Submission deadline for pre-symposium review:     20th November, 2020 Submission deadline for draft papers:             15th January, 2021 Symposium dates:                                  17-19th February, 2021 * We strongly encourage authors to submit their work for the first deadline.   Authors whose papers are accepted for presentation, but not immediately for the   proceedings in this first round, will have almost two months to address the   reviewers' concerns. Papers submitted for the first deadline will also have   priority for the presentation slots at the symposium. Visit tfp2021.org for more information. From P.Achten at cs.ru.nl Tue Sep 29 14:45:11 2020 From: P.Achten at cs.ru.nl (Peter Achten) Date: Tue, 29 Sep 2020 16:45:11 +0200 Subject: [Haskell-cafe] [TFPIE'21] First Call For Papers: Trends in Functional Programming *in Education* 2021, 16 February (with Lambda Days 2021 & TFP 2021) Message-ID: <4e9187ea-51dd-36ee-10b2-bdb4d9806110@cs.ru.nl> --------------------------------    TFPIE 2021 Call for papers -------------------------------- https://wiki.tfpie.science.ru.nl/TFPIE2021#TFPIE_2021 (February 16 2021, co-organized with TFP 2021 and Lambda Days 2021) The goal of the International Workshops on Trends in Functional Programming in Education is to gather researchers, professors, teachers, and all professionals that use or are interested in the use of functional programming in education. TFPIE aims to be a venue where novel ideas, classroom-tested ideas, and work in progress on the use of functional programming in education are discussed. The one-day workshop will foster a spirit of open discussion by having a review process for publication after the workshop. TFPIE 2021 welcomes submissions in the above mentioned areas. This year many teaching programmes have had to make a rapid transition to online teaching, and we explicitly solicit papers that explore this area of teaching functional programming. Topics of interest include, but are not limited to: -  FP and beginning CS students -  FP and Computational Thinking -  FP and Artificial Intelligence -  FP in Robotics -  FP and Music -  Advanced FP for undergraduates -  FP in graduate education -  Engaging students in research using FP -  FP in Programming Languages -  FP in the high school curriculum -  FP as a stepping stone to other CS topics -  FP and Philosophy -  The pedagogy of teaching FP -  FP and e-learning: MOOCs, automated assessment etc. -  Best Lectures - more details below In addition to papers, we are requesting best lecture presentations. What's your best lecture topic in an FP related course? Do you have a fun way to present FP concepts to novices or perhaps an especially interesting presentation of a difficult topic? In either case, please consider sharing it. Best lecture topics will be selected for presentation based on a short abstract describing the lecture and its interest to TFPIE attendees. The length of the presentation should be comparable to that of a paper. On top of the lecture itself, the presentation can also provide commentary on the lecture. Submissions Potential presenters are invited to submit an extended abstract (4-6 pages) or a draft paper (up to 20 pages) in EPTCS style. The authors of accepted presentations will have their preprints and their slides made available on the workshop's website. Papers and abstracts can be submitted via easychair at the following link: https://easychair.org/conferences/?conf=tfpie2021 After the workshop, presenters are invited to submit (a revised version of) their article for review. The PC will select the best articles. We plan to publish them in the Electronic Proceedings in Theoretical Computer Science (EPTCS). Articles rejected for presentation and extended abstracts will not be formally reviewed by the PC. Dates -  Submission deadline: January 11 2021, Anywhere on Earth. -  Notification: January 15 2021 -  Workshop: February 16 2021 -  Submission for formal review: April 20 2021, Anywhere on Earth. -  Notification of full article: June 7 2021 -  Camera ready: July 1st 2021 Program Committee (under construction) - Peter Achten,    Radboud University, Netherlands (chair) - Edwin Brady,     University of St Andrews, UK - Laura Castro,    Universidade da Coruña, Spain - Stephen Chang,   University of Massachusetts Boston, USA - Youyou Cong,     Tokyo Institute of Technology, Japan - Matthew Flatt,   University of Utah, USA - Alex Gerdes,     University of Gothenburg, Sweden - Prabhakar Ragde, University of Waterloo, Canada - Melinda Tóth,    Eötvös Loránd University, Hungary Registration TFPIE is part of Lambda Days. Please visit the Lambda Days 2021 pages when registration information becomes available. Registration is mandatory for at least one author of every paper that is presented at the workshop. Only papers that have been presented at TFPIE may be submitted to the post-reviewing process. Information on Lambda Days is available at https://www.lambdadays.org/lambdadays2021 Information on TFP         is available at http://tfp2021.org From dj112358 at outlook.com Tue Sep 29 15:03:01 2020 From: dj112358 at outlook.com (David James) Date: Tue, 29 Sep 2020 15:03:01 +0000 Subject: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks In-Reply-To: References: <80de4375-cf5a-8d2a-421b-cc23d5002fd2@gmail.com> , Message-ID: No problem! My original comment was meant to be somewhat tongue in cheek (as maybe was Branimir’s response?). My website (IMHO) is way beyond trivial, and was probably up and running after a mere 6 months! (I’d never built a website before, or used e.g. GitHub, Docker, Ubuntu, AWS, etc, so they were all a bit of a challenge too). Anyway, enough of this. If anyone does have comments on the page, it would probably be best to add them on the discussion page alongside the article (now here). Unless there are any strong objections, I’ll overwrite the main page with the reworked one in a week or so, and I’ll be doing some final reviewing and tweaking of the page in the meantime. (And thanks to Bryan and Hilco for their comments). Thanks! David. From: Tom Smeding Sent: 29 September 2020 14:46 To: haskell-cafe at haskell.org Subject: Re: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks Hi all, Branimir wrote: > Two years is not enough to do anything serious in Haskell. That is beyond demo programs. This statement carries a lot of assumptions, some of which may be true in this case, some of which may not -- I don't know David James -- but I think it isn't good to let this statement stand on its own like that. If a student who has never programmed before starts learning Haskell, they will not be able to succeed professionally after just two years unless they have other useful background knowledge and/or talent (let's not discuss whether talent is a thing or not). However, the email by David James did not sound like he is a beginner in the act of programming; indeed, with experience in imperative (C++, VB), declarative (SQL), functional (Lisp) and logic (Prolog) programming languages, that certainly does not count as "has never programmed before". And I think we can agree that someone with sufficient experience in programming at large can learn to use Haskell effectively in two years (which is quite a long time, even if it's irregular practice). At least, I strongly believe this until proven otherwise. If that is indeed not the case, then something is seriously wrong on Haskell's side, as Gregory has noted below. David: I haven't read your rewritten State monad tutorial, for which my apologies; also apologies for kind of derailing this thread. Cheers, and have a great day, Tom Smeding P.S. This message wasn't accepted by the mailing list earlier, so was just sent to some people personally. Sorry for duplicate emails. On Tue, 29 Sep 2020 at 15:14, Gregory Guthrie > wrote: If this is true, Haskell will probably never move up into even the top 40 of used languages, which would be too bad! And it would be interesting to see why – certainly people find good productivity in the mainstream IP languages in even 6-9 months. (Look at salaries from bootcamp placements.) Currently Haskell is significantly below Cobol, Fortran, Lisp, and Pascal in the language usage/ratings. :-( Dr. Gregory Guthrie Maharishi International University ---------------------------------------------------------------- From: Haskell-Cafe > On Behalf Of Branimir Maksimovic Sent: Tuesday, September 29, 2020 7:17 AM To: haskell-cafe at haskell.org Subject: Re: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks Two years is not enough to do anything seroius in Haskell. That is beyond demo programs. Greets, Branimir. On 9/28/20 6:37 PM, David James wrote: Hello – I’m proposing to restructure this, and I have a draft here. I’ve given my main reasons for the restructuring at the top of the draft page. I’d very much like feedback before updating the real page, especially if people don’t like the new one much. I’m new here, so should probably say a bit about myself. I’ve been learning-by-doing Haskell for about two years. Sometimes I feel I’m starting to get it, but these feelings don’t usually last long. I’m certainly not an expert, and don’t have a PhD in Very Clever Things. I do have quite an extensive IT background, originally programming in Lisp and Prolog, then (sadly) C++, VB and SQL but have had quite a long break from programming (doing dumb things like architecting systems and project managing) before looking at Haskell. I built this website and this library, just for my own amusement. Apologies if this isn’t the right place to send this. (But then where is?) I’ve already put a note on the page itself about the new draft, but I’ve no idea whether anyone will notice it. Thanks very much, David. _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post. _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post. -------------- next part -------------- An HTML attachment was scrubbed... URL: From eborsboom at fpcomplete.com Tue Sep 29 18:43:37 2020 From: eborsboom at fpcomplete.com (Emanuel Borsboom) Date: Tue, 29 Sep 2020 18:43:37 +0000 Subject: [Haskell-cafe] ANN: first release candidate for stack-2.5.x Message-ID: Download binaries from here: https://github.com/commercialhaskell/stack/releases/tag/v2.5.0.1 **Changes since v2.3.3** Major changes: * Add the `snapshot-location-base` yaml configuration option, which allows to override the default location of snapshot configuration files. This option affects how snapshot synonyms (LTS/Nightly) are expanded to URLs by the `pantry` library. * `docker-network` configuration key added to overwrite docker `--net` arg Behavior changes: * File watching now takes into account specified targets, old behavior could be restored using the new flag `--watch-all` [#5310](https://github.com/commercialhaskell/stack/issues/5310) Other enhancements: * `stack ls dependencies json` now includes fields `sha256` and `size` for dependencies of `type` `archive` in `location`. [#5280](https://github.com/commercialhaskell/stack/issues/5280) * Build failures now show a hint to scroll up to the corresponding section [#5279](https://github.com/commercialhaskell/stack/issues/5279) * Customisable output styles (see `stack --help` and the `--stack-colors` option, and `stack ls stack-colors --help`) now include `info`, `debug`, `other-level`, `secondary` and `highlight`, used with verbose output. Bug fixes: * Fix `stack test --coverage` when using Cabal 3 * `stack new` now generates PascalCase'd module name correctly. [#5376](https://github.com/commercialhaskell/stack/issues/5376) * Connection issues to Casa server no longer cause builds to failure. Casa acts only as an optimizing cache layer, not a critical piece of infrastructure. * Fix modified time busting caches by always calculating sha256 digest during the build process. [#5125](https://github.com/commercialhaskell/stack/issues/5125) -------------- next part -------------- An HTML attachment was scrubbed... URL: From olf at aatal-apotheke.de Tue Sep 29 21:17:16 2020 From: olf at aatal-apotheke.de (Olaf Klinke) Date: Tue, 29 Sep 2020 23:17:16 +0200 Subject: [Haskell-cafe] Proposed "Restructuring" of State Monad page in Haskell Wikibooks Message-ID: > Hello – I’m proposing to restructure this< > https://en.wikibooks.org/wiki/Haskell/Understanding_monads/State>;, > and I have a draft here< > https://en.wikibooks.org/wiki/Davjam2:Example/StateMonad>;. I’ve > given my main reasons for the restructuring at the top of the draft > page. > > I’d very much like feedback before updating the real page, especially > if people don’t like the new one much. +1 for the new version by David. In addition to the content already present, I'd like to see examples (and exercises! these are great!) of State used with other common monadic combinators. You covered sequence, but what about mapM a.k.a. traverse, what about foldM? Can filterM be put to good use with state monads? You could introduce another type TurnstileInput = Push | Coin and combine pushS, coinS into a single function of type TurnstileInput -> State TurnstileState TurnstileOutput which can be used in mapM. Your exercises of different people can then be implemented as mapM of a single function (namely the FSM) over finite sequences of TurnstileInputs. As a side-note, Data.Traversable.mapAccumR is implemented using a StateR that passes the state backwards. Only an Applicative instance is given in Data.Functor.Utils. I wonder whether this is a lawful monad or not. The comment says so, but I don't see how. Here is a departure from the otherwise very concrete and down-to-earth explanation of State, but a fun exercise nevertheless: Implement return and (>>=) for type State s a = s -> (s,a) using the following two functions and the Functor instance of ((->) s). iso :: ((s,a) -> b) -> a -> s -> b iso uncurried = curry (uncurried . Data.Tuple.swap) iso' :: (a -> s -> b) -> (s,a) -> b -- inverse of iso iso' curried = uncurry (flip curried) Olaf From danburton.email at gmail.com Tue Sep 29 21:26:20 2020 From: danburton.email at gmail.com (Dan Burton) Date: Tue, 29 Sep 2020 17:26:20 -0400 Subject: [Haskell-cafe] [Haskell] [ANNOUNCE] Glasgow Haskell Compiler 9.0.1-alpha1 released In-Reply-To: References: <873631g1e2.fsf@smart-cactus.org> Message-ID: That's great news! Thank you for your hard work. Does this address the Windows issue with GHC 8.10.2? Will there still be a GHC 8.10.3 release? See: https://gitlab.haskell.org/ghc/ghc/-/issues/18550 (Pardon the dupe; I had the wrong address on haskell-cafe) -- Dan Burton On Tue, Sep 29, 2020 at 5:23 PM Dan Burton wrote: > That's great news! Thank you for your hard work. > > Does this address the Windows issue with GHC 8.10.2? Will there still be a > GHC 8.10.3 release? > See: https://gitlab.haskell.org/ghc/ghc/-/issues/18550 > > -- Dan Burton > > > On Mon, Sep 28, 2020 at 3:16 PM Ben Gamari wrote: > >> Hello all, >> >> The GHC team is very pleased to announce the availability of the first >> alpha release in the GHC 9.0 series. Source and binary distributions are >> available at the usual place: >> >> https://downloads.haskell.org/ghc/9.0.1-alpha1/ >> >> This first alpha comes quite a bit later than expected. However, we have >> done a significant amount of testing on this pre-release and therefore >> hope to be able to move forward quickly with a release candidate next >> week and with a final release in mid-October. >> >> GHC 9.0.1 will bring a number of new features: >> >> * A first cut of the new LinearTypes language extension [1], allowing >> use of linear function syntax and linear record fields. >> >> * A new bignum library (ghc-bignum), allowing GHC to be more easily >> used with integer libraries other than GMP. >> >> * Improvements in code generation, resulting in considerable >> performance improvements in some programs. >> >> * Improvements in pattern-match checking, allowing more precise >> detection of redundant cases and reduced compilation time. >> >> * Implementation of the "simplified subsumption" proposal [2] >> simplifying the type system and paving the way for QuickLook >> impredicativity in GHC 9.2. >> >> * Implementation of the QualifiedDo extension [3], allowing more >> convenient overloading of `do` syntax. >> >> * Improvements in compilation time. >> >> And many more. See the release notes [4] for a full accounting of the >> changes in this release. >> >> Do note that there are a few things that we expect will change before >> the final release: >> >> * We expect to sort out a notarization workflow for Apple Darwin, >> allowing our binary distributions to be used on macOS Catalina >> without hassle. >> >> Until this has been sorted out Catalina users can exempt the >> current macOS binary distribution from the notarization requirement >> themselves by running `xattr -cr .` on the unpacked tree before >> running `make install`. >> >> * We will likely transition the Alpine binary distribution to be fully >> statically-linked, providing a convenient, distribution-independent >> packaging option for Linux users. >> >> * We will be merging a robust solution for #17760 which will introduce >> a new primitive, `keepAlive#`, to the `base` library, subsuming >> most uses of `touch#`. >> >> As always, do test this release and open tickets for whatever issues you >> encounter. To help with this, we will be publishing a blog post >> describing use of our new `head.hackage` infrastructure to ease testing >> of larger projects with Hackage dependencies later this week. >> >> Cheers, >> >> - Ben >> >> >> [1] >> https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0111-linear-types.rst >> [2] >> https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0287-simplify-subsumption.rst >> [3] >> https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0216-qualified-do.rst >> [4] >> https://downloads.haskell.org/ghc/9.0.1-alpha1/docs/html/users_guide/9.0.1-notes.html >> _______________________________________________ >> Haskell mailing list >> Haskell at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From capn.freako at gmail.com Wed Sep 30 18:52:56 2020 From: capn.freako at gmail.com (David Banas) Date: Wed, 30 Sep 2020 11:52:56 -0700 Subject: [Haskell-cafe] I think I discovered my first Monoid instance Message-ID: > > But we're talking about xor for the Word64 type, so it is slightly more > complicated to prove that it has a unit. Here's a proof in Agda: -- Agda proof that zero is the unit (i.e. - left and right identity) -- for the XOR operation. import Relation.Binary.PropositionalEquality as Eq open Eq using (_≡_; refl; cong; sym) open import Data.Nat using (ℕ; zero; suc; _+_; _*_; _∸_; _^_) -- Binary data. data Bin : Set where ⟨⟩ : Bin _O : Bin → Bin _I : Bin → Bin _xor_ : Bin → Bin → Bin ⟨⟩ xor y = y (x O) xor ⟨⟩ = x O (x O) xor (y O) = (x xor y) O (x O) xor (y I) = (x xor y) I (x I) xor ⟨⟩ = x I (x I) xor (y O) = (x xor y) I (x I) xor (y I) = (x xor y) O lemma : ∀ (b : Bin) ------------ → b xor ⟨⟩ ≡ b lemma ⟨⟩ = refl lemma (b O) = refl lemma (b I) = refl _ : ( ⟨⟩ I O O I O I I O ) xor ( ⟨⟩ O I I I O O O O ) ---------------------- ≡ ( ⟨⟩ I I I O O I I O ) _ = refl -- Left & right identity proofs. postulate binO : (⟨⟩ O) ≡ ⟨⟩ -- Just accomodating a syntactical nuisance. xor-idₗ : ∀ (b : Bin) ---------------- → (⟨⟩ O) xor b ≡ b xor-idₗ ⟨⟩ rewrite binO = refl xor-idₗ (b O) = refl xor-idₗ (b I) = refl xor-idᵣ : ∀ (b : Bin) ---------------- → b xor (⟨⟩ O) ≡ b xor-idᵣ ⟨⟩ rewrite binO = refl xor-idᵣ (b O) rewrite lemma b = refl xor-idᵣ (b I) rewrite lemma b = refl -db -------------- next part -------------- An HTML attachment was scrubbed... URL: