Combine prisms to focus on a value regardless of a branch - purescript

I have a sum type of possible outcomes, and in every outcome there is a certain "Result" that I want to focus on. I know how to get that "Result" from each of the outcomes (I have a bunch of prisms for that), but I don't know how to combine these prisms so that I can grab the "Result" from the whole sumtype, without worrying which case I'm on.
Simplified example:
type OneAnother = Either Int Int
exampleOneAnother :: OneAnother
exampleOneAnother = Left 10
_one :: Prism' OneAnother Int
_one = _Left
_another :: Prism' OneAnother Int
_another = _Right
_result :: Lens' OneAnother Int
_result = ???
-- How can I combine _one and _another to get result regardless whether its left or right ?

Once a prism comes to focus, it loses the context. So I don't see a way to define _result in terms of _one and _another. But you can certainly do better than resorting to unsafePartial:
import Data.Lens.Lens (Lens', lens)
import Data.Profunctor.Choice ((|||), (+++))
type OneAnother = Either Int Int
_result :: Lens' OneAnother Int
_result = lens getter setter
where
getter = identity ||| identity
setter e x = (const x +++ const x) e
Stole this from the profunctor-lens repository:
-- | Converts a lens into the form that `lens'` accepts.
lensStore :: forall s t a b . ALens s t a b -> s -> Tuple a (b -> t)
lensStore l = withLens l (lift2 Tuple)
It isn't exported somehow. With that help, the following solution should be generic enough:
import Prelude
import Control.Apply (lift2)
import Data.Lens.Common
import Data.Lens.Lens
import Data.Lens.Prism
import Data.Profunctor.Choice ((|||), (+++))
import Data.Tuple
_result :: Lens' OneAnother Int
_result = lens getter setter
where
getter = identity ||| identity
setter e x = (const x +++ const x) e
lensStore :: forall s t a b . ALens s t a b -> s -> Tuple a (b -> t)
lensStore l = withLens l (lift2 Tuple)
data ABC
= A Int
| B (Tuple Boolean Int)
| C OneAnother
lensABCInt :: Lens' ABC Int
lensABCInt = lens' case _ of
A i -> map A <$> lensStore identity i
B i -> map B <$> lensStore _2 i
C i -> map C <$> lensStore _result i
Here ABC is your target sum type. As long as its each variant has a lens, you have a lens for it as a whole.

This is the best I've got so far. Yes, unsafePartial, explicit case matching ... Really hope there is something better.
_result :: Lens' OneAnother Int
_result = lens getter setter
where
getter :: OneAnother -> Int
getter x#(Left _) = unsafePartial $ fromJust $ preview _one x
getter x#(Right _) = unsafePartial $ fromJust $ preview _another x
setter :: OneAnother -> Int -> OneAnother
setter (Left _) x = review _one x
setter (Right _) x = review _another x

Related

Mapping homogeneous record type

Suppose we have a record type that is homogeneous.
type RecI = { a :: Int, b :: Int, c :: Int, d :: Int, e :: Int }
We want to get from it type with the same keys but different value type:
type RecS = { a :: String, b :: String, c :: String, d :: String, e :: String }
Is it possible to get RecS type without explicitly defining all the keys from RecI?
And the second part of the question, what is the best way to implement mapping function from one type to another:
mapItoS :: (Int -> String) -> RecI -> RecS
?
To get a free-ish conversion from Int to String at type level, just give your record a parameter, then instantiate it with Int to get RecI and with String to get RecS:
type Rec a = { a :: a, b :: a, c :: a, d :: a, e :: a }
type RecI = Rec Int
type RecS = Rec String
To implement mapItoS, you can first convert to a Foreign.Object using fromHomogeneous, then map the function over it, then convert back to the record.
Unfortunately there is no toHomogeneous function, because in general you can't be sure that the Foreign.Object actually contains all required keys. But no matter: in this particular case you can be sure that it does, so you can get away with unsafeCoerce:
mapItoS :: forall a b. (a -> b) -> Rec a -> Rec b
mapItoS f = fromHomogeneous >>> map f >>> unsafeCoerce
A small self plug which is strictly relevant to the question :-P I've just published a library which provides many instances which allow a PureScripter to work with homogeneous Record and Variant:
https://pursuit.purescript.org/packages/purescript-homogeneous
I think it should have much better inference than solutions like heterogeneous. Please check it out and let me know what do you think.

Combining lists of variants of different types in purescript

I have been playing around with Purescript as a means of experimenting with programming in a Haskell-like language with row and column polymorphism.
In particular, using Purescript's variant package, I was trying to write an operation to combine two lists list of "notes" sequentially, where the two lists have possibly disjoint sets of variants. The idea here is to allow users to be able to combine scores declared on some sub-set of notes to be automatically combined with scores containing a super-set, without preforming any explicit conversions.
For example, given the following boilerplate to make working with the variants easier:
import Data.Variant
import Prim.Row
data A = A
data B = B
data C = C
data D = D
data E = E
data F = F
data G = G
data H = H
_A = SProxy :: SProxy "_A"
_B = SProxy :: SProxy "_B"
_C = SProxy :: SProxy "_C"
_D = SProxy :: SProxy "_D"
_E = SProxy :: SProxy "_E"
_F = SProxy :: SProxy "_F"
_G = SProxy :: SProxy "_G"
I can define the following types:
type CNatural = Variant (
_A :: A,
_B :: B,
_C :: C,
_D :: D,
_E :: E,
_F :: F,
_G :: G)
type CPentatonic = Variant (
_A :: A,
_C :: C,
_D :: D,
_E :: E,
_G :: G)
And given those, I can define two scores:
score1 :: Array CPentatonic
score1 = [inj _A A,inj _C C]
score2 :: Array CNatural
score2 = [inj _C C, inj _B B]
So, to combine them, I want a function of signature
combine :: forall v w u. Union w v u =>
Array (Variant v)
-> Array (Variant w)
-> Array (Variant u)
However, my attempt at this:
combine x y = (map expand x) <> (map expand y)
yields
No type class instance was found for
Prim.Row.Union v2
t3
u4
The instance head contains unknown type variables. Consider adding a type annotation.
If I try changing the Union v w u constraint to a Union w v u constraint, the error goes back and forth between the first map expand, and the second map expand, but nothing I do seems to resolve both constraints, even if I have both a Union v w u and a Union w v u constraint, which I thought would have been redundant.
Is there something I'm missing? Is it possible to do something like this in Purescript using the variant library?
If you want to produce Variant u by using expand you have to require that both v and w are subrows of u:
module Main where
import Prelude
import Data.Variant (expand, Variant)
import Prim.Row (class Union)
combine :: forall v v_ w w_ u. Union w w_ u => Union v v_ u =>
Array (Variant v)
-> Array (Variant w)
-> Array (Variant u)
combine arr1 arr2 = map expand arr1 <> map expand arr2
Here is a working gist loaded into try.purescript.

Purescript: Could not match type

In the REPL this works:
> mm n = (\n -> n * 2) <$> n
> mm (2:3:Nil)
(4 : 6 : Nil)
in a file this compiles and I can run it:
squareOf ls =
map (\n -> n * n) ls
however when I add a type definition to that function
squareOf :: List Int -> Int
squareOf ls =
map (\n -> n * n) ls
I get an error:
Could not match type
List Int
with type
Int
while checking that type t0 t1
is at least as general as type Int
while checking that expression (map (\n ->
(...) n
)
)
ls
has type Int
in value declaration squareOf
where t0 is an unknown type
t1 is an unknown type
I tried changing the signature to a type alias of the list, and also I tried a forall definition with no luck.
If I inspect the definition created when I don't put signatures in my function I get:
forall t2 t3. Functor t2 => Semiring t3 => t2 t3 -> t2 t3
Can anyone explain why my signature is incorrect and also why am I getting this signature for the function?
Cheers
Edit: Thanks for the comments, updating the fn definition so it returns a List Int as well, and , of course it solves the problem
Assuming you're repl function is the behaviour you're after, you've missed out the map operator (<$>) in your later definitions.
Your repl function (with variables renamed for clarity) has the type:
mm :: forall f. Functor f => f Int -> f Int
mm ns = (\n -> n * 2) <$> ns
Which is to say: mm maps "times two" to something that is mappable" (i.e. a Functor)
Aside: you could be more concise/clear in your definition here:
mm :: forall f. Functor f => f Int -> f Int
mm = map (_*2)
This is similar to your squareOf definition, only now you're squaring so your use of (*) is more general:
squareOf :: forall f. Functor f => Semiring n => f n -> f n
squareOf = map \n -> n * n
Because (*) is a member of the Semiring typeclass.
But the signature you gave it suggests you're after some kind of fold? Let me know what output you expect from your squareOf function and I'll update the answer accordingly.
Here is map:
class Functor f where
map :: forall a b. (a -> b) -> f a -> f b
Narrowing to List Int and Int -> Int, the compiler infers
map :: (Int -> Int) -> List Int -> List Int
So, in squareOf, the expression reduces to a list of integers, not an integer. That is why the compiler complains.

why does the compiler give error on the following haskell code?

I am trying to study the type classes in haskell. I write the following script and the raised an error. I am unable to understand why the compiler thinks of v as an concrete type while it is just a parameter for class Boxer.
data Box1 a b = Box1 Double a [b]
class Boxer v where
foo :: (v a b) -> Double
instance Boxer (Box1 a b) where
foo (Box1 r s t) = r
it raises an error in line 7:8:
Couldn't match type `v' with `Box1'
`v' is a rigid type variable bound by
the type signature for foo :: v a b -> Double at file1.hs:4:10
Expected type: v a b
Actual type: Box1 a b
Relevant bindings include
foo :: v a b -> Double (bound at file1.hs:7:3)
In the pattern: Box1 r s t
In an equation for `foo': foo (Box1 r s t) = r
Failed, modules loaded: none.
In your instance, the compiler has to instantiate v with Box1 a b. In particular, it has to instantiate v a b with something like (Box1 a b) a b – except both a variables come from a different place; they're actually disambiguated to (Box1 a b) a1 b1. Which is the same as Box1 a b a1 b1.
foo :: Box1 a b a1 b1 -> Double
Does that make any sense?
The problem is that you're confusing a (type) function, namely Box1, with the result of applying said function to some type arguments. The kinds don't match:
GHCi> :k Boxer
Boxer :: (* -> * -> *) -> Constraint
GHCi> :k (Box1 Int String)
Box1 Int String :: *
* -> * -> * is the kind of a type function / type constructor with two arguments, so that's what Boxer needs. Whereas Box1 a b is simply a type, with no arguments. Doesn't match! OTOH,
GHCi> :k Box1
Box1 :: * -> * -> *
The particular problem was being caused by improper indentation. Though there was another thing I was doing wrong. So the following version compiled:
data Box1 a b = Box1 Double a [b]
class Boxer v where
foo :: (v a b) -> Double
instance Boxer Box1 where
foo (Box1 r s t) = r

How to define an abstract data type like "data MyMath = MyNum Num"?

I want to define a new abstract data type which is either a general Number or a Division construct. How would I do that in Haskell?
My first approach was:
data MyMath = MyNum Num
| Div MyMath MyMath
The problem is that the compiler complains about "Num" which is not a data type but a type class. So my second thought would be to solve the problem like this:
data MyMath = MyNum Int
| MyNum Float
| Div MyMath MyMath
But this would not work either as MyNum is used twice which is not allowed, additionaly this approach would not really be polymorphic. So what is the solution to this problem?
EDIT2: After (again) reading the answers I tried to use GADT data constructors. This is some artificial example code:
5 data MyMathExpr a where
6 MyNumExpr :: Num a => a -> MyMathExpr a
7 MyAddExpr :: MyMathExpr b -> MyMathExpr c -> MyMathExpr (b, c)
8 deriving instance Show(MyMathExpr a)
9 deriving instance Eq(MyMathExpr a)
10
11 data MyMathVal a where
12 MyMathVal :: Num a => a -> MyMathVal a
13 deriving instance Show(MyMathVal a)
14 deriving instance Eq(MyMathVal a)
15
16 foo :: MyMathExpr a -> MyMathVal a
17 foo (MyNumExpr num) = MyMathVal num
18 foo (MyAddExpr num1 num2) = MyMathVal (l + r)
19 where (MyMathVal l) = foo num1
20 (MyMathVal r) = foo num2
But something is wrong with line number 18:
test.hs:18:40:
Couldn't match type `b' with `(b, c)'
`b' is a rigid type variable bound by
a pattern with constructor
MyAddExpr :: forall b c.
MyMathExpr b -> MyMathExpr c -> MyMathExpr (b, c),
in an equation for `foo'
at test.hs:18:6
In the first argument of `(+)', namely `l'
In the first argument of `MyMathVal', namely `(l + r)'
In the expression: MyMathVal (l + r)
The same goes for `c'. I guess it's a stupid mistake which I just don't see. Do you?
This solves the problem you're addressing in the code, but doesn't cover for the boolean.
If you want to use a class constraint in a data declaration, you do it the way that you would with any other function:
data (Num a) => MyMath a = MyMath {x :: a}
You can use existential quantification for that:
> let data MyMath = forall n. Num n => MyNum n
> :t MyNum 3
MyNum 3 :: MyMath
> :t MyNum 3.5
MyNum 3.5 :: MyMath
There are many ways to do it. One way is with GADTs:
{-# LANGUAGE GADTs #-}
data MyMath where
MyNum :: Num a => a -> MyMath
MyBool :: Bool -> MyMath
Another way with GADTs:
{-# LANGUAGE GADTs #-}
data MyMath a where
MyNum :: Num a => a -> MyMath a
MyBool :: Num a => Bool -> MyMath a
As has been mentioned, your attempts don't involve any booleans, but I'll go with the text of your question instead.
You don't have to invent this type, check out Either in the Prelude. What you're looking for is thus Either a Bool, where you want a to be an instance of Num. If you want to actually enforce this, ready the edit below.
Edit: If you don't want to use Either, you can do data MyMath a = MyNum a | MyBool Bool. Now you can enforce a being an instance of Num if you want to, but you may want to consider this SO question and this answer to it first. There's really no need to enforce the instance for the data type; just do it for the functions using it intsead.