You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**Description**
Currently, using structs in Copilot requires defining instances of
several type classes. Defining those instances is unnecessarily
cumbersome, and it exposes the user to a substantial amount of Haskell
even when they want to stay at the level of the Copilot language.
It would be helpful to facilitate generating instances of `Typed` and
`Struct` for struct types / record types, by providing functions that
implement the default behavior and only requiring users to write their
own functions if they wanted to deviate from those defaults.
**Type**
- Feature: Improve usability of the language.
**Additional context**
None.
**Requester**
- Ivan Perez.
**Method to check presence of bug**
Not applicable (not a bug).
**Expected result**
Introduce functions that implement the default behavior for the methods
in the `Typed` and `Struct` type classes, so that users can rely on them
when implementing their own instances.
The following Dockerfile checks that the new methods can be used in
place of the hand-coded implementations of the Struct and Typed class
methods, by comparing the results produced against known expectations,
and it also that the examples updated to illustrate the new features
compile successfully, after which it prints the message "Success":
```
--- Dockerfile
FROM ubuntu:focal
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get install --yes libz-dev
RUN apt-get install --yes git
RUN apt-get install --yes wget
RUN mkdir -p $HOME/.ghcup/bin
RUN wget https://downloads.haskell.org/~ghcup/0.1.19.2/x86_64-linux-ghcup-0.1.19.2 -O $HOME/.ghcup/bin/ghcup
RUN chmod a+x $HOME/.ghcup/bin/ghcup
ENV PATH=$PATH:/root/.ghcup/bin/
ENV PATH=$PATH:/root/.cabal/bin/
RUN apt-get install --yes curl
RUN apt-get install --yes gcc g++ make libgmp3-dev
RUN apt-get install --yes pkg-config
RUN apt-get install --yes z3
SHELL ["/bin/bash", "-c"]
RUN ghcup install ghc 9.4
RUN ghcup install cabal 3.2
RUN ghcup set ghc 9.4.8
RUN cabal update
ADD Structs.hs /tmp/Structs.hs
ADD main.c /tmp/main.c
ADD expected /tmp/expected
CMD git clone $REPO \
&& cd $NAME \
&& git checkout $COMMIT \
&& cabal v1-sandbox init \
&& cabal v1-install alex happy --constraint='happy < 2' \
&& cabal v1-install copilot**/ \
&& cabal v1-exec -- runhaskell /tmp/Structs.hs \
&& mv /tmp/main.c . \
&& gcc main.c structs.c -o main \
&& ./main > actual \
&& diff actual /tmp/expected \
&& cabal v1-exec -- runhaskell copilot/examples/Structs.hs \
&& cabal v1-exec -- runhaskell copilot/examples/StructsUpdateField.hs \
&& cabal v1-exec -- runhaskell copilot/examples/what4/Structs.hs \
&& echo Success
--- Structs.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where
import Copilot.Compile.C99
import Data.Type.Equality as DE
import Language.Copilot
import GHC.Generics
-- * Struct type definitions
data SoA = SoA
{ arr :: Field "arr" SoB
}
deriving Generic
instance Struct SoA where
typeName = typeNameDefault
toValues = toValuesDefault
instance Typed SoA where
typeOf = typeOfDefault
data SoB = SoB
{ arr2 :: Field "arr2" (Array 3 Float)
}
deriving Generic
instance Struct SoB where
typeName = typeNameDefault
toValues = toValuesDefault
instance Typed SoB where
typeOf = typeOfDefault
data SoC = SoC
{ arr3 :: Field "arr3" Int32
}
deriving Generic
-- | Struct with a custom value for updates
instance Struct SoC where
typeName = typeNameDefault
toValues = toValuesDefault
updateField = updateFieldDefault
instance Typed SoC where
typeOf = typeOfDefault
-- Sample values
-- We purposefully leave some type signatures out to check that GHC can infer
-- them.
-- SoAs
a1 = SoA $ Field $ SoB $ Field $ array [0, 1, 2]
b1 = SoB $ Field $ array [10, 20, 30]
b2 = SoB $ Field $ array [40, 50, 60]
c1 = SoC $ Field 5
soa :: Stream SoA
soa = constant a1
soa1 = soa ## arr =: recursiveArray
soa2 = soa ## arr =: constant b1
soarr = soa ## arr =: soa # arr
soc1 = constant c1 ## arr3 =: counter
soc2 = constant c1 ## arr3 =$ (+5)
recursiveArray :: Stream SoB
recursiveArray = [b1, b2] ++ recursiveArray
counter :: Stream Int32
counter = [55] ++ (counter + 1)
spec :: Spec
spec = do
trigger "arrays" (soa1 # arr # arr2 ! 1 /= 60) [arg soa, arg soa1, arg soa2]
trigger "arrays2" true [arg soc1]
trigger "arrays3" true [arg soc2]
main :: IO ()
main = do
spec' <- reify spec
compile "structs" spec'
--- main.c
void arrays( struct so_a * arrays_arg0
, struct so_a * arrays_arg1
, struct so_a * arrays_arg2
) {
printf("0: %f %f %f\n"
, arrays_arg0->arr.arr2[0]
, arrays_arg0->arr.arr2[1]
, arrays_arg0->arr.arr2[2]);
printf("1: %f %f %f\n"
, arrays_arg1->arr.arr2[0]
, arrays_arg1->arr.arr2[1]
, arrays_arg1->arr.arr2[2]);
printf("2: %f %f %f\n"
, arrays_arg2->arr.arr2[0]
, arrays_arg2->arr.arr2[1]
, arrays_arg2->arr.arr2[2]);
}
void arrays2(struct so_c * arrays2_arg0) {
printf("soc1: %d\n", arrays2_arg0->arr3);
}
void arrays3(struct so_c * arrays2_arg0) {
printf("soc2: %d\n", arrays2_arg0->arr3);
}
int main() {
step();
step();
step();
step();
}
--- expected
0: 0.000000 1.000000 2.000000
1: 10.000000 20.000000 30.000000
2: 10.000000 20.000000 30.000000
soc1: 55
soc2: 10
0: 0.000000 1.000000 2.000000
1: 40.000000 50.000000 60.000000
2: 10.000000 20.000000 30.000000
soc1: 56
soc2: 10
0: 0.000000 1.000000 2.000000
1: 10.000000 20.000000 30.000000
2: 10.000000 20.000000 30.000000
soc1: 57
soc2: 10
0: 0.000000 1.000000 2.000000
1: 40.000000 50.000000 60.000000
2: 10.000000 20.000000 30.000000
soc1: 58
soc2: 10
```
**Solution implemented**
Introduce four definitions in `copilot-core:Copilot.Core.Type` that
provide default implementations for the fields in the `Typed` and
`Struct` type classes for types that are `Generic`. Let users use those
types so long as they make their structs `Generic`.
Update the examples in the examples directory in `copilot` that shows
how the new implementations can be used.
**Further notes**
Discussion #540 proposes multiple alternative implementations. Although
the preferred solution long-term is 2, the mechanism proposed is not
backwards compatible, so it helps to first introduce a mechanism to
simplify the implementation without breaking existing code. We can, upon
release of this feature, communicate to users that Copilot will move in
that direction if appropriate, and encourage users to transition their
code so that we can introduce breaking changes 3 versions later per our
deprecation policy.
0 commit comments