git.iko.soy/rowtype-yoga/purescript-yoga-react-native
on main help sign in
WIP: React Native bindings development
commitcf948529f3288fca020f726b40bf0d0353926e0c authorMark <mark.eibes@gmail.com> dateMon 16 Feb 2026, 23:07 (7 months ago) parenta41f350
17 files changed+93−76
docs/src/Docs/Pages/InputControls.purs+4−4 docs/src/Docs/Pages/QuickStart.purs+1−1 example-macos/src/Demo/AiMl.purs+6−6 example-macos/src/Demo/Chat.purs+8−9 example-macos/src/Demo/InputControls.purs+2−2 example-macos/src/Demo/Overlays.purs+14−16 example-macos/src/Demo/SystemServices.purs+27−29 example-macos/src/Demo/TextEditing.purs−1 src/Yoga/React/Native/MacOS/Alert.purs+2−1 src/Yoga/React/Native/MacOS/Button.js+9−1 src/Yoga/React/Native/MacOS/Button.purs+3−2 src/Yoga/React/Native/MacOS/HelpButton.js+9−1 src/Yoga/React/Native/MacOS/HelpButton.purs+3−2 src/Yoga/React/Native/MacOS/Slider.purs+1 src/Yoga/React/Native/MacOS/Switch.purs+1 src/Yoga/React/Native/MacOS/TextField.purs+1 src/Yoga/React/Native/MacOS/UserNotification.purs+2−1
docs/src/Docs/Pages/InputControls.purs+4−4
@@ -30,7 +30,7 @@ button = L.componentDoc "nativeButton" "Yoga.React.Native.MacOS.Button (nativeBu
3030 """nativeButton
3131 { title: "Click me"
3232 , bezelStyle: T.push
33− , onPress: handler_ doSomething
33+ , onPress: doSomething
3434 }"""
3535 [ propsTable
3636 [ { name: "title", type_: "String", description: "Button label text (mutually exclusive with sfSymbol)" }
@@ -39,7 +39,7 @@ button = L.componentDoc "nativeButton" "Yoga.React.Native.MacOS.Button (nativeBu
3939 , { name: "destructive", type_: "Boolean", description: "Red destructive styling" }
4040 , { name: "primary", type_: "Boolean", description: "Primary action button" }
4141 , { name: "buttonEnabled", type_: "Boolean", description: "Enable/disable the button" }
42− , { name: "onPress", type_: "EventHandler", description: "Press callback" }
42+ , { name: "onPress", type_: "Effect Unit", description: "Press callback" }
4343 ]
4444 ]
4545
@@ -228,8 +228,8 @@ colorWell = L.componentDoc "nativeColorWell" "Yoga.React.Native.MacOS.ColorWell
228228helpButton :: Nut
229229helpButton = L.componentDoc "nativeHelpButton" "Yoga.React.Native.MacOS.HelpButton (nativeHelpButton)"
230230 """nativeHelpButton
231− { onPress: handler_ showHelp }"""
231+ { onPress: showHelp }"""
232232 [ propsTable
233− [ { name: "onPress", type_: "EventHandler", description: "Press callback" }
233+ [ { name: "onPress", type_: "Effect Unit", description: "Press callback" }
234234 ]
235235 ]
docs/src/Docs/Pages/QuickStart.purs+1−1
@@ -57,7 +57,7 @@ import Yoga.React.Native.MacOS.Types as T
5757myButton = nativeButton
5858 { title: "Click me"
5959 , bezelStyle: T.push
60− , onPress: handler_ doSomething
60+ , onPress: doSomething
6161 , style: Style.style { height: 24.0 }
6262 }"""
6363
example-macos/src/Demo/AiMl.purs+6−6
@@ -68,13 +68,13 @@ speechRecognitionDemo = component "SpeechRecognitionDemo" \dp -> React.do
6868 [ nativeButton
6969 { title: if speech.listening then "Listening..." else "Start"
7070 , bezelStyle: T.push
71− , onPress: handler_ speech.start
71+ , onPress: speech.start
7272 , style: Style.style { height: 24.0, width: 130.0 }
7373 }
7474 , nativeButton
7575 { title: "Stop"
7676 , bezelStyle: T.push
77− , onPress: handler_ speech.stop
77+ , onPress: speech.stop
7878 , style: Style.style { height: 24.0, width: 80.0, marginLeft: 8.0 }
7979 }
8080 ]
@@ -101,7 +101,7 @@ naturalLanguageDemo = component "NaturalLanguageDemo" \dp -> React.do
101101 [ nativeButton
102102 { title: "Language"
103103 , bezelStyle: T.push
104− , onPress: handler_ $ launchAff_ do
104+ , onPress: launchAff_ do
105105 lang <- detectLanguage txt
106106 liftEffect (setResult ("Language: " <> lang))
107107 , style: Style.style { height: 24.0, width: 110.0 }
@@ -109,7 +109,7 @@ naturalLanguageDemo = component "NaturalLanguageDemo" \dp -> React.do
109109 , nativeButton
110110 { title: "Sentiment"
111111 , bezelStyle: T.push
112− , onPress: handler_ $ launchAff_ do
112+ , onPress: launchAff_ do
113113 score <- analyzeSentiment txt
114114 liftEffect (setResult ("Sentiment: " <> show score))
115115 , style: Style.style { height: 24.0, width: 110.0, marginLeft: 8.0 }
@@ -117,7 +117,7 @@ naturalLanguageDemo = component "NaturalLanguageDemo" \dp -> React.do
117117 , nativeButton
118118 { title: "Tokenize"
119119 , bezelStyle: T.push
120− , onPress: handler_ $ launchAff_ do
120+ , onPress: launchAff_ do
121121 tokens <- tokenize txt
122122 liftEffect (setResult ("Tokens: " <> joinWith ", " tokens))
123123 , style: Style.style { height: 24.0, width: 110.0, marginLeft: 8.0 }
@@ -138,7 +138,7 @@ cameraDemo = component "CameraDemo" \dp -> React.do
138138 , nativeButton
139139 { title: if on then "Stop" else "Start"
140140 , bezelStyle: T.push
141− , onPress: handler_ (setOn (not on))
141+ , onPress: setOn (not on)
142142 , style: Style.style { height: 24.0, width: 100.0 }
143143 }
144144 ]
example-macos/src/Demo/Chat.purs+8−9
@@ -189,7 +189,7 @@ chatDemo = component "ChatDemo" \dp -> React.do
189189 , nativeButton
190190 { title: ""
191191 , bezelStyle: T.toolbar
192− , onPress: handler_ (selectRoom r.roomId)
192+ , onPress: selectRoom r.roomId
193193 , style: Style.style { position: "absolute", top: 0.0, left: 0.0, right: 0.0, bottom: 0.0, opacity: 0.0 }
194194 }
195195 ]
@@ -219,10 +219,9 @@ chatDemo = component "ChatDemo" \dp -> React.do
219219 [ nativeButton
220220 { title: "☺"
221221 , bezelStyle: T.toolbar
222− , onPress: handler_
223− ( if reactPopover == Just idx then setReactPopover Nothing
224− else setReactPopover (Just idx)
225− )
222+ , onPress:
223+ if reactPopover == Just idx then setReactPopover Nothing
224+ else setReactPopover (Just idx)
226225 , style: Style.style { height: 20.0, width: 24.0 }
227226 }
228227 , view { style: tw "flex-row items-center p-2" <> Style.style { height: 40.0 } }
@@ -230,7 +229,7 @@ chatDemo = component "ChatDemo" \dp -> React.do
230229 nativeButton
231230 { title: emoji
232231 , bezelStyle: T.toolbar
233− , onPress: handler_ (reactToMessage idx emoji)
232+ , onPress: reactToMessage idx emoji
234233 , style: Style.style { height: 32.0, width: 32.0 }
235234 }
236235 )
@@ -253,7 +252,7 @@ chatDemo = component "ChatDemo" \dp -> React.do
253252 , nativeButton
254253 { title: ""
255254 , bezelStyle: T.toolbar
256− , onPress: handler_ (scrollToMessage origIdx)
255+ , onPress: scrollToMessage origIdx
257256 , style: Style.style { position: "absolute", top: 0.0, left: 0.0, right: 0.0, bottom: 0.0, opacity: 0.0 }
258257 }
259258 ]
@@ -311,7 +310,7 @@ chatDemo = component "ChatDemo" \dp -> React.do
311310 , nativeButton
312311 { title: "✕"
313312 , bezelStyle: T.toolbar
314− , onPress: handler_ (setReplyingTo Nothing)
313+ , onPress: setReplyingTo Nothing
315314 , style: Style.style { height: 20.0, width: 24.0 }
316315 }
317316 ]
@@ -368,7 +367,7 @@ chatDemo = component "ChatDemo" \dp -> React.do
368367 , nativeButton
369368 { sfSymbol: "paperplane.fill"
370369 , bezelStyle: T.toolbar
371− , onPress: handler_ (sendMessage rid inputText)
370+ , onPress: sendMessage rid inputText
372371 , style: Style.style { height: 28.0, width: 36.0, marginLeft: 8.0 }
373372 }
374373 ]
example-macos/src/Demo/InputControls.purs+2−2
@@ -52,13 +52,13 @@ buttonDemo = component "ButtonDemo" \dp -> React.do
5252 { title: "Say Hello"
5353 , bezelStyle: T.push
5454 , primary: true
55− , onPress: handler_ (setStatus "Hello from PureScript!")
55+ , onPress: setStatus "Hello from PureScript!"
5656 , style: Style.style { height: 24.0, width: 140.0 }
5757 }
5858 , nativeButton
5959 { title: "Reset"
6060 , bezelStyle: T.push
61− , onPress: handler_ (setStatus "Ready")
61+ , onPress: setStatus "Ready"
6262 , style: Style.style { height: 24.0, width: 100.0, marginLeft: 8.0 }
6363 }
6464 , label dp.dimFg status
example-macos/src/Demo/Overlays.purs+14−16
@@ -33,7 +33,7 @@ alertDemo = component "AlertDemo" \dp -> pure do
3333 , nativeButton
3434 { title: "Show Alert"
3535 , bezelStyle: T.push
36− , onPress: handler_ (macosAlert { style: T.warning, title: "Are you sure?", message: "This action cannot be undone.", buttons: [ "Cancel", "OK" ] })
36+ , onPress: macosAlert { style: T.warning, title: "Are you sure?", message: "This action cannot be undone.", buttons: [ "Cancel", "OK" ] }
3737 , style: Style.style { height: 24.0, width: 120.0 } <> tw "mb-4"
3838 }
3939 ]
@@ -48,7 +48,7 @@ sheetDemo = component "SheetDemo" \dp -> React.do
4848 , nativeButton
4949 { title: "Show Sheet"
5050 , bezelStyle: T.push
51− , onPress: handler_ (setVisible true)
51+ , onPress: setVisible true
5252 , style: Style.style { height: 24.0, width: 120.0 } <> tw "mb-2"
5353 }
5454 , nativeSheet
@@ -61,7 +61,7 @@ sheetDemo = component "SheetDemo" \dp -> React.do
6161 , nativeButton
6262 { title: "Dismiss"
6363 , bezelStyle: T.push
64− , onPress: handler_ (setVisible false)
64+ , onPress: setVisible false
6565 , style: Style.style { height: 24.0, width: 100.0 }
6666 }
6767 ]
@@ -85,7 +85,7 @@ popoverDemo = component "PopoverDemo" \dp -> React.do
8585 [ nativeButton
8686 { title: if visible then "Hide Popover" else "Show Popover"
8787 , bezelStyle: T.push
88− , onPress: handler_ (setVisible (not visible))
88+ , onPress: setVisible (not visible)
8989 , style: Style.style { height: 24.0, width: 140.0 }
9090 }
9191 , view { style: tw "p-4" <> Style.style { width: 200.0, height: 80.0 } }
@@ -135,18 +135,16 @@ menuDemo = component "MenuDemo" \dp -> React.do
135135 [ nativeButton
136136 { title: "Show Menu"
137137 , bezelStyle: T.push
138− , onPress: handler_
139− ( macosShowMenu
140− { items:
141− [ { title: "New File", id: "new" }
142− , { title: "Open...", id: "open" }
143− , { title: "-", id: "sep" }
144− , { title: "Save", id: "save" }
145− , { title: "Save As...", id: "saveAs" }
146− ]
147− , onSelectItem: \itemId -> setResult ("Menu: " <> itemId)
148− }
149− )
138+ , onPress: macosShowMenu
139+ { items:
140+ [ { title: "New File", id: "new" }
141+ , { title: "Open...", id: "open" }
142+ , { title: "-", id: "sep" }
143+ , { title: "Save", id: "save" }
144+ , { title: "Save As...", id: "saveAs" }
145+ ]
146+ , onSelectItem: \itemId -> setResult ("Menu: " <> itemId)
147+ }
150148 , style: Style.style { height: 24.0, width: 120.0 }
151149 }
152150 , if result == "" then mempty
example-macos/src/Demo/SystemServices.purs+27−29
@@ -14,14 +14,13 @@ import Prelude
1414
1515import Demo.Shared (DemoProps, desc, scrollWrap, sectionTitle)
1616import React.Basic (JSX)
17−import React.Basic.Events (handler_)
17+
1818import React.Basic.Hooks (useState', (/\))
1919import React.Basic.Hooks as React
2020import Yoga.React (component)
2121import Yoga.React.Native (tw, view)
2222import Yoga.React.Native.MacOS.Button (nativeButton)
2323import Yoga.React.Native.MacOS.ColorPanel (macosShowColorPanel)
24−import Yoga.React.Native.MacOS.Events as E
2524import Yoga.React.Native.MacOS.FontPanel (macosShowFontPanel)
2625import Yoga.React.Native.MacOS.Pasteboard (copyToClipboard)
2726import Yoga.React.Native.MacOS.QuickLook (macosQuickLook)
@@ -43,7 +42,7 @@ clipboardDemo = component "ClipboardDemo" \dp -> pure do
4342 [ nativeButton
4443 { title: "Copy 'Hello'"
4544 , bezelStyle: T.push
46− , onPress: handler_ (copyToClipboard "Hello from PureScript!")
45+ , onPress: copyToClipboard "Hello from PureScript!"
4746 , style: Style.style { height: 24.0, width: 120.0 }
4847 }
4948 ]
@@ -57,7 +56,7 @@ shareDemo = component "ShareDemo" \dp -> pure do
5756 , nativeButton
5857 { title: "Share Text"
5958 , bezelStyle: T.push
60− , onPress: handler_ (macosShare [ "Hello from PureScript React Native!" ])
59+ , onPress: macosShare [ "Hello from PureScript React Native!" ]
6160 , style: Style.style { height: 24.0, width: 120.0 } <> tw "mb-2"
6261 }
6362 ]
@@ -70,7 +69,7 @@ notificationsDemo = component "NotificationsDemo" \dp -> pure do
7069 , nativeButton
7170 { title: "Send Notification"
7271 , bezelStyle: T.push
73− , onPress: handler_ (macosNotify { title: "PureScript", body: "Hello from React Native macOS!" })
72+ , onPress: macosNotify { title: "PureScript", body: "Hello from React Native macOS!" }
7473 , style: Style.style { height: 24.0, width: 160.0 } <> tw "mb-2"
7574 }
7675 ]
@@ -84,19 +83,19 @@ soundDemo = component "SoundDemo" \dp -> pure do
8483 [ nativeButton
8584 { title: "Glass"
8685 , bezelStyle: T.push
87− , onPress: handler_ (macosPlaySound "Glass")
86+ , onPress: macosPlaySound "Glass"
8887 , style: Style.style { height: 24.0, width: 80.0 }
8988 }
9089 , nativeButton
9190 { title: "Ping"
9291 , bezelStyle: T.push
93− , onPress: handler_ (macosPlaySound "Ping")
92+ , onPress: macosPlaySound "Ping"
9493 , style: Style.style { height: 24.0, width: 80.0, marginLeft: 8.0 }
9594 }
9695 , nativeButton
9796 { title: "Beep"
9897 , bezelStyle: T.push
99− , onPress: handler_ macosBeep
98+ , onPress: macosBeep
10099 , style: Style.style { height: 24.0, width: 80.0, marginLeft: 8.0 }
101100 }
102101 ]
@@ -113,22 +112,21 @@ statusBarDemo = component "StatusBarDemo" \dp -> React.do
113112 [ nativeButton
114113 { title: if active then "Remove" else "Add to Menu Bar"
115114 , bezelStyle: T.push
116− , onPress: handler_
117− ( if active then do
118− macosRemoveStatusBarItem
119− setActive false
120− else do
121− macosSetStatusBarItem
122− { title: ""
123− , sfSymbol: "swift"
124− , menuItems:
125− [ { id: "about", title: "About PureScript RN" }
126− , { id: "sep", title: "-" }
127− , { id: "quit", title: "Quit" }
128− ]
129− }
130− setActive true
131− )
115+ , onPress:
116+ if active then do
117+ macosRemoveStatusBarItem
118+ setActive false
119+ else do
120+ macosSetStatusBarItem
121+ { title: ""
122+ , sfSymbol: "swift"
123+ , menuItems:
124+ [ { id: "about", title: "About PureScript RN" }
125+ , { id: "sep", title: "-" }
126+ , { id: "quit", title: "Quit" }
127+ ]
128+ }
129+ setActive true
132130 , style: Style.style { height: 24.0, width: 180.0 }
133131 }
134132 ]
@@ -143,7 +141,7 @@ quickLookDemo = component "QuickLookDemo" \dp -> pure do
143141 [ nativeButton
144142 { title: "Preview /etc/hosts"
145143 , bezelStyle: T.push
146− , onPress: handler_ (macosQuickLook "/etc/hosts")
144+ , onPress: macosQuickLook "/etc/hosts"
147145 , style: Style.style { height: 24.0, width: 180.0 }
148146 }
149147 ]
@@ -158,7 +156,7 @@ colorPanelDemo = component "ColorPanelDemo" \dp -> pure do
158156 [ nativeButton
159157 { title: "Show Color Panel"
160158 , bezelStyle: T.push
161− , onPress: handler_ macosShowColorPanel
159+ , onPress: macosShowColorPanel
162160 , style: Style.style { height: 24.0, width: 180.0 }
163161 }
164162 ]
@@ -173,7 +171,7 @@ fontPanelDemo = component "FontPanelDemo" \dp -> pure do
173171 [ nativeButton
174172 { title: "Show Font Panel"
175173 , bezelStyle: T.push
176− , onPress: handler_ macosShowFontPanel
174+ , onPress: macosShowFontPanel
177175 , style: Style.style { height: 24.0, width: 180.0 }
178176 }
179177 ]
@@ -196,13 +194,13 @@ speechDemo = component "SpeechDemo" \dp -> React.do
196194 [ nativeButton
197195 { title: "Speak"
198196 , bezelStyle: T.push
199− , onPress: handler_ (say txt)
197+ , onPress: say txt
200198 , style: Style.style { height: 24.0, width: 100.0 }
201199 }
202200 , nativeButton
203201 { title: "Stop"
204202 , bezelStyle: T.push
205− , onPress: handler_ stopSpeaking
203+ , onPress: stopSpeaking
206204 , style: Style.style { height: 24.0, width: 100.0, marginLeft: 8.0 }
207205 }
208206 ]
example-macos/src/Demo/TextEditing.purs−1
@@ -8,7 +8,6 @@ import React.Basic.Hooks (useState', (/\))
88import React.Basic.Hooks as React
99import Yoga.React (component)
1010import Yoga.React.Native (tw, view)
11−import Yoga.React.Native.MacOS.Events as E
1211import Yoga.React.Native.MacOS.TextField (nativeTextField)
1312import Yoga.React.Native.MacOS.TextEditor (nativeTextEditor)
1413import Yoga.React.Native.Style as Style
src/Yoga/React/Native/MacOS/Alert.purs+2−1
@@ -8,6 +8,7 @@ import Prelude
88import Effect (Effect)
99import Effect.Uncurried (EffectFn1, runEffectFn1)
1010import Prim.Row (class Union)
11+import Unsafe.Coerce (unsafeCoerce)
1112import Yoga.React.Native.MacOS.Types (AlertStyle)
1213
1314type AlertProps =
@@ -24,4 +25,4 @@ macosAlert
2425 . Union given missing AlertProps
2526 => { | given }
2627 -> Effect Unit
27−macosAlert = runEffectFn1 alertImpl
28+macosAlert r = runEffectFn1 alertImpl (unsafeCoerce r)
src/Yoga/React/Native/MacOS/Button.js+9−1
@@ -1,2 +1,10 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const buttonImpl = requireNativeComponent("NativeButton");
3+const NativeButton = requireNativeComponent("NativeButton");
4+export const buttonImpl = React.forwardRef((props, ref) => {
5+ const nativeProps = { ...props };
6+ if (typeof props.onPress === "function") {
7+ nativeProps.onPress = () => props.onPress();
8+ }
9+ return React.createElement(NativeButton, { ...nativeProps, ref });
10+});
src/Yoga/React/Native/MacOS/Button.purs+3−2
@@ -1,7 +1,8 @@
11module Yoga.React.Native.MacOS.Button (nativeButton, NativeButtonAttributes) where
22
3+import Prelude
4+import Effect (Effect)
35import React.Basic (ReactComponent)
4−import React.Basic.Events (EventHandler)
56import Yoga.React.Native.Attributes (BaseAttributes)
67import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
78import Yoga.React.Native.MacOS.Types (BezelStyle)
@@ -18,5 +19,5 @@ type NativeButtonAttributes = BaseAttributes
1819 , destructive :: Boolean
1920 , primary :: Boolean
2021 , buttonEnabled :: Boolean
21− , onPress :: EventHandler
22+ , onPress :: Effect Unit
2223 )
src/Yoga/React/Native/MacOS/HelpButton.js+9−1
@@ -1,2 +1,10 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const helpButtonImpl = requireNativeComponent("MacOSHelpButton");
3+const NativeHelpButton = requireNativeComponent("MacOSHelpButton");
4+export const helpButtonImpl = React.forwardRef((props, ref) => {
5+ const nativeProps = { ...props };
6+ if (typeof props.onPress === "function") {
7+ nativeProps.onPress = () => props.onPress();
8+ }
9+ return React.createElement(NativeHelpButton, { ...nativeProps, ref });
10+});
src/Yoga/React/Native/MacOS/HelpButton.purs+3−2
@@ -3,8 +3,9 @@ module Yoga.React.Native.MacOS.HelpButton
33 , NativeHelpButtonAttributes
44 ) where
55
6+import Prelude
7+import Effect (Effect)
68import React.Basic (ReactComponent)
7−import React.Basic.Events (EventHandler)
89import Yoga.React.Native.Attributes (BaseAttributes)
910import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
1011
@@ -14,5 +15,5 @@ nativeHelpButton :: FFINativeComponent_ NativeHelpButtonAttributes
1415nativeHelpButton = createNativeElement_ helpButtonImpl
1516
1617type NativeHelpButtonAttributes = BaseAttributes
17− ( onPress :: EventHandler
18+ ( onPress :: Effect Unit
1819 )
src/Yoga/React/Native/MacOS/Slider.purs+1
@@ -1,5 +1,6 @@
11module Yoga.React.Native.MacOS.Slider (nativeSlider, NativeSliderAttributes) where
22
3+import Prelude
34import Effect (Effect)
45import React.Basic (ReactComponent)
56import Yoga.React.Native.Attributes (BaseAttributes)
src/Yoga/React/Native/MacOS/Switch.purs+1
@@ -1,5 +1,6 @@
11module Yoga.React.Native.MacOS.Switch (nativeSwitch, NativeSwitchAttributes) where
22
3+import Prelude
34import Effect (Effect)
45import React.Basic (ReactComponent)
56import Yoga.React.Native.Attributes (BaseAttributes)
src/Yoga/React/Native/MacOS/TextField.purs+1
@@ -1,5 +1,6 @@
11module Yoga.React.Native.MacOS.TextField (nativeTextField, NativeTextFieldAttributes) where
22
3+import Prelude
34import Effect (Effect)
45import React.Basic (ReactComponent)
56import Yoga.React.Native.Attributes (BaseAttributes)
src/Yoga/React/Native/MacOS/UserNotification.purs+2−1
@@ -8,6 +8,7 @@ import Prelude
88import Effect (Effect)
99import Effect.Uncurried (EffectFn1, runEffectFn1)
1010import Prim.Row (class Union)
11+import Unsafe.Coerce (unsafeCoerce)
1112
1213type NotifyProps =
1314 ( title :: String
@@ -21,4 +22,4 @@ macosNotify
2122 . Union given missing NotifyProps
2223 => { | given }
2324 -> Effect Unit
24−macosNotify = runEffectFn1 notifyImpl
25+macosNotify r = runEffectFn1 notifyImpl (unsafeCoerce r)