7

Hello how can you enforce GHC type for functions such as Data.Text.read or the =~ operator from Text.Regex.Posix when composing methods?

example:
a=["1.22","3.33","5.55"]

Without point free:
b= map (\x-> read x ::Double) a

How to enforce a type for read with point free notation ?

b=map read::Double a or
b= map (read . f1 .f2 .f3... . fn )::Double a (when composing methods)
where f1 , f2 ...fn are methods

Or better how do you specify the read type when it belongs in a chain of methods ,BUT not at the end of the chain ! :
b=map (f2 . read . f1 ) a

2 Answers 2

11

The best way in modern Haskell is with a type application.

Prelude> :set -XTypeApplications 
Prelude> map (read @Double) ["1.22","3.33","5.55"]
[1.22,3.33,5.55]
Prelude> map (read @Int) ["1.22","3.33","5.55"]
[*** Exception: Prelude.read: no parse

This works because read has the signature

read :: ∀ a . Read a => String -> a

and therefore read @Double specialises a ~ Double and thus

read @Double :: String -> Double
7
  • FWIW I'm not a fan of this extension, so at least one person disagrees this is "The best way..." ;)
    – jberryman
    Commented Sep 11, 2018 at 16:00
  • 1
    You wouldn't care to explain why you don't like it? Commented Sep 11, 2018 at 16:20
  • sure, sorry: 1) suddenly class constraints stop being a set (the ordering matters; do I now have to worry about re-ordering constraints in my own libraries (imagine my editor automatically alphabetizes them) to stop breakage for users who choose to use type application? Might it even cause bugs that pass the typechecker? I'm honestly not sure). 2) Reuse of @ for some totally unrelated meaning 3) applying a function to a type doesn't really make sense (I think this is a GHC internal implementation detail (dictionary passing) leaking upwards). It might be I have wrong ideas about it though
    – jberryman
    Commented Sep 11, 2018 at 21:14
  • With 1) you definitely have a point. 2) and 3) don't seem that critical to me. Commented Sep 11, 2018 at 21:59
  • @jberryman: #1 is at least no worse than other languages like C++ where you always write explicit quantifiers and their order matters. Order will be important for Dependent Haskell too, since you can’t freely move pi types around like foralls. Explicit quantifiers also have the advantage of enabling ScopedTypeVariables without changing a signature. Honestly, it seems a bit silly in retrospect that Haskell imposed lexical restrictions to differentiate type constructors & variables just to avoid forall (or sigils like OCaml’s ')—@ would’ve been a perfect ASCII substitute for , too!
    – Jon Purdy
    Commented Sep 12, 2018 at 9:53
3

read has type String -> a, so read x has type a. Just like forcing read x to have type Double instead of a with read x :: Double, you can force read to have type String -> Double instead:

b = map (read :: String -> Double) a
3
  • So if i have a chain of composed methods map ( f1 . read . f2 ) a i could say : map ( f2 . read::t0->t1 . f0 ) a where f0::t0 and read::t1 ? Commented Sep 11, 2018 at 16:18
  • 1
    Probably not, because read . f0 doesn't really make sense if f0 isn't a function that returns a String. You can't make wholesale changes to the type of read; you can only specialize its type.
    – chepner
    Commented Sep 11, 2018 at 16:25
  • 1
    @BercoviciAdrian With appropriate parentheses, that is syntactically correct: map (f2 . (read :: t0 -> t1) . f0) a. That's likely to be a type error except in some very special circumstances where the context constrains t0 and t1 a lot, though. Commented Sep 11, 2018 at 18:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.