├── .gitignore ├── Setup.hs ├── SwHaLib └── include │ └── module.modulemap ├── tutorial ├── squared.png ├── empty-app.png ├── xcode-ib-add-label.png ├── xcode-add-to-no-targets.png ├── xcode-choose-executable.png ├── xcode-cocoa-template-icon.png ├── xcode-header-search-paths.png ├── xcode-ib-add-label-outlet.png ├── xcode-target-membership.png ├── xcode-new-copy-files-phase.png ├── xcode-ib-label-outlet-dialog.png ├── xcode-select-framework-target.png ├── xcode-embed-swift-standard-libs.png ├── xcode-swift-module-search-paths.png ├── xcode-cocoa-framework-template-icon.png ├── xcode-create-project-language-swift.png ├── xcode-drag-swifthaskell-executable.png ├── xcode-new-framework-run-script-phase.png └── xcode-copy-files-swifthaskell-executable.png ├── SwiftHaskell ├── include │ ├── module.modulemap │ └── Main_stub.h ├── Info.plist └── Assets.xcassets │ └── AppIcon.appiconset │ └── Contents.json ├── src ├── Lib.hs └── Main.hs ├── SwiftAppLibrary ├── SwiftAppLibrary.m ├── SwiftAppLibrary.h ├── Info.plist ├── AppDelegate.swift └── Base.lproj │ └── MainMenu.xib ├── SwiftHaskellLibrary.cabal ├── link-deps.sh ├── LICENSE ├── stack.yaml ├── SwiftHaskell.xcodeproj └── project.pbxproj └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | build/ 3 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /SwHaLib/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module SwHaLib { 2 | export * 3 | } 4 | -------------------------------------------------------------------------------- /tutorial/squared.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/squared.png -------------------------------------------------------------------------------- /tutorial/empty-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/empty-app.png -------------------------------------------------------------------------------- /SwiftHaskell/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module SwiftHaskell { 2 | header "Main_stub.h" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /tutorial/xcode-ib-add-label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-ib-add-label.png -------------------------------------------------------------------------------- /tutorial/xcode-add-to-no-targets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-add-to-no-targets.png -------------------------------------------------------------------------------- /tutorial/xcode-choose-executable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-choose-executable.png -------------------------------------------------------------------------------- /tutorial/xcode-cocoa-template-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-cocoa-template-icon.png -------------------------------------------------------------------------------- /tutorial/xcode-header-search-paths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-header-search-paths.png -------------------------------------------------------------------------------- /tutorial/xcode-ib-add-label-outlet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-ib-add-label-outlet.png -------------------------------------------------------------------------------- /tutorial/xcode-target-membership.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-target-membership.png -------------------------------------------------------------------------------- /tutorial/xcode-new-copy-files-phase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-new-copy-files-phase.png -------------------------------------------------------------------------------- /tutorial/xcode-ib-label-outlet-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-ib-label-outlet-dialog.png -------------------------------------------------------------------------------- /tutorial/xcode-select-framework-target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-select-framework-target.png -------------------------------------------------------------------------------- /tutorial/xcode-embed-swift-standard-libs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-embed-swift-standard-libs.png -------------------------------------------------------------------------------- /tutorial/xcode-swift-module-search-paths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-swift-module-search-paths.png -------------------------------------------------------------------------------- /src/Lib.hs: -------------------------------------------------------------------------------- 1 | module Lib where 2 | 3 | import Foreign.C 4 | 5 | foreign export ccall square :: CInt -> CInt 6 | 7 | square :: CInt -> CInt 8 | square x = x * x 9 | -------------------------------------------------------------------------------- /tutorial/xcode-cocoa-framework-template-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-cocoa-framework-template-icon.png -------------------------------------------------------------------------------- /tutorial/xcode-create-project-language-swift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-create-project-language-swift.png -------------------------------------------------------------------------------- /tutorial/xcode-drag-swifthaskell-executable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-drag-swifthaskell-executable.png -------------------------------------------------------------------------------- /tutorial/xcode-new-framework-run-script-phase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-new-framework-run-script-phase.png -------------------------------------------------------------------------------- /tutorial/xcode-copy-files-swifthaskell-executable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanotech/swift-haskell-tutorial/HEAD/tutorial/xcode-copy-files-swifthaskell-executable.png -------------------------------------------------------------------------------- /SwiftAppLibrary/SwiftAppLibrary.m: -------------------------------------------------------------------------------- 1 | #import "SwiftAppLibrary.h" 2 | 3 | @interface AClassInThisFramework : NSObject @end 4 | @implementation AClassInThisFramework @end 5 | 6 | void runNSApplication(void) { 7 | NSApplication *app = [NSApplication sharedApplication]; 8 | NSBundle *bundle = [NSBundle bundleForClass:[AClassInThisFramework class]]; 9 | NSArray *topObjects; 10 | [[[NSNib alloc] initWithNibNamed:@"MainMenu" bundle:bundle] 11 | instantiateWithOwner:app topLevelObjects:&topObjects]; 12 | [app run]; 13 | } 14 | -------------------------------------------------------------------------------- /SwiftHaskell/include/Main_stub.h: -------------------------------------------------------------------------------- 1 | #include "HsFFI.h" 2 | #ifdef __cplusplus 3 | extern "C" { 4 | #endif 5 | extern HsInt32 square(HsInt32 a1); 6 | extern HsWord64 countBytes(HsWord8 a1, HsPtr a2, HsWord64 a3); 7 | extern HsPtr getSequence(HsPtr a1); 8 | extern void callbackExample(HsFunPtr a1); 9 | extern void contextCallbackExample(HsPtr a1, HsFunPtr a2, HsFunPtr a3); 10 | extern HsFunPtr makeMultiplier(HsInt32 a1); 11 | extern void freeMultiplier(HsFunPtr a1); 12 | extern HsInt32 Main_d9Dt(StgStablePtr the_stableptr, HsInt32 a1); 13 | #ifdef __cplusplus 14 | } 15 | #endif 16 | 17 | -------------------------------------------------------------------------------- /SwiftAppLibrary/SwiftAppLibrary.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftAppLibrary.h 3 | // SwiftAppLibrary 4 | // 5 | // Created by NanoTech on 2017-02-08. 6 | // Copyright © 2017 nanotech. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for SwiftAppLibrary. 12 | FOUNDATION_EXPORT double SwiftAppLibraryVersionNumber; 13 | 14 | //! Project version string for SwiftAppLibrary. 15 | FOUNDATION_EXPORT const unsigned char SwiftAppLibraryVersionString[]; 16 | 17 | FOUNDATION_EXPORT void runNSApplication(void); 18 | 19 | // In this header, you should import all the public headers of your framework using statements like #import 20 | -------------------------------------------------------------------------------- /SwiftAppLibrary/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2017 nanotech. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SwiftHaskellLibrary.cabal: -------------------------------------------------------------------------------- 1 | name: SwiftHaskellLibrary 2 | version: 0.1.0.0 3 | -- synopsis: 4 | -- description: 5 | homepage: https://github.com/nanotech/swift-haskell-tutorial#readme 6 | license: BSD2 7 | license-file: LICENSE 8 | author: nanotech 9 | maintainer: nanotech@nanotechcorp.net 10 | copyright: 2017 nanotech 11 | -- category: 12 | build-type: Simple 13 | extra-source-files: 14 | cabal-version: >=1.10 15 | 16 | source-repository head 17 | type: git 18 | location: https://github.com/nanotech/swift-haskell-tutorial 19 | 20 | executable SwiftHaskell 21 | default-language: Haskell2010 22 | hs-source-dirs: src 23 | main-is: Main.hs 24 | ghc-options: -threaded -framework-path build 25 | ld-options: -rpath @executable_path/../Frameworks 26 | frameworks: SwiftAppLibrary 27 | build-depends: base >= 4.7 && < 5 28 | , bytestring >= 0.10 && < 0.11 29 | , text >= 1.2 && < 1.3 30 | -------------------------------------------------------------------------------- /SwiftHaskell/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2017 nanotech. All rights reserved. 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /SwiftHaskell/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /link-deps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | 4 | EXECUTABLE_NAME=SwiftHaskell 5 | DIST_DIR="$(stack path --dist-dir)" 6 | GHC_VERSION="$(stack exec -- ghc --numeric-version)" 7 | GHC_LIB_DIR="$(stack path --compiler-bin)/../lib/ghc-$GHC_VERSION" 8 | STUB_BUILD_DIR="${DIST_DIR}/build/${EXECUTABLE_NAME}/${EXECUTABLE_NAME}-tmp" 9 | STUB_MODULE_DIR="${EXECUTABLE_NAME}/include" 10 | STUB_MODULE_MAP="${STUB_MODULE_DIR}/module.modulemap" 11 | 12 | # Create a module map from the generated Haskell 13 | # FFI export headers for importing into Swift. 14 | mkdir -p "${STUB_MODULE_DIR}" 15 | NL=" 16 | " 17 | module_map="module ${EXECUTABLE_NAME} {${NL}" 18 | for h in $(find "${STUB_BUILD_DIR}" -name '*.h'); do 19 | h_filename="${h/$STUB_BUILD_DIR\//}" 20 | cp "$h" "${STUB_MODULE_DIR}/" 21 | module_map="${module_map} header \"${h_filename}\"${NL}" 22 | done 23 | module_map="${module_map} export *${NL}" 24 | module_map="${module_map}}" 25 | echo "${module_map}" > "${STUB_MODULE_MAP}" 26 | 27 | # Symlink to the current GHC's header directory from a more 28 | # convenient place for Xcode to find. 29 | mkdir -p build/ghc 30 | ln -sf "${GHC_LIB_DIR}/include" build/ghc/ 31 | 32 | # Symlink to the Haskell executable for Xcode. 33 | ln -sf "../${DIST_DIR}/build/${EXECUTABLE_NAME}/${EXECUTABLE_NAME}" build/ 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, nanotech 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by 'stack init' 2 | # 3 | # Some commonly used options have been documented as comments in this file. 4 | # For advanced use and comprehensive documentation of the format, please see: 5 | # http://docs.haskellstack.org/en/stable/yaml_configuration/ 6 | 7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version. 8 | # A snapshot resolver dictates the compiler version and the set of packages 9 | # to be used for project dependencies. For example: 10 | # 11 | # resolver: lts-3.5 12 | # resolver: nightly-2015-09-21 13 | # resolver: ghc-7.10.2 14 | # resolver: ghcjs-0.1.0_ghc-7.10.2 15 | # resolver: 16 | # name: custom-snapshot 17 | # location: "./custom-snapshot.yaml" 18 | resolver: lts-7.19 19 | 20 | # User packages to be built. 21 | # Various formats can be used as shown in the example below. 22 | # 23 | # packages: 24 | # - some-directory 25 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz 26 | # - location: 27 | # git: https://github.com/commercialhaskell/stack.git 28 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a 29 | # - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a 30 | # extra-dep: true 31 | # subdirs: 32 | # - auto-update 33 | # - wai 34 | # 35 | # A package marked 'extra-dep: true' will only be built if demanded by a 36 | # non-dependency (i.e. a user package), and its test suites and benchmarks 37 | # will not be run. This is useful for tweaking upstream packages. 38 | packages: 39 | - '.' 40 | # Dependency packages to be pulled from upstream that are not in the resolver 41 | # (e.g., acme-missiles-0.3) 42 | extra-deps: [] 43 | 44 | # Override default flag values for local packages and extra-deps 45 | flags: {} 46 | 47 | # Extra package databases containing global packages 48 | extra-package-dbs: [] 49 | 50 | # Control whether we use the GHC we find on the path 51 | # system-ghc: true 52 | # 53 | # Require a specific version of stack, using version ranges 54 | # require-stack-version: -any # Default 55 | # require-stack-version: ">=1.3" 56 | # 57 | # Override the architecture used by stack, especially useful on Windows 58 | # arch: i386 59 | # arch: x86_64 60 | # 61 | # Extra directories used by stack for building 62 | # extra-include-dirs: [/path/to/dir] 63 | # extra-lib-dirs: [/path/to/dir] 64 | # 65 | # Allow a newer minor version of GHC than the snapshot specifies 66 | # compiler-check: newer-minor -------------------------------------------------------------------------------- /SwiftAppLibrary/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftAppLibrary 4 | // 5 | // Created by NanoTech on 2017-02-09. 6 | // Copyright © 2017 nanotech. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import SwiftHaskell 11 | 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | @IBOutlet weak var window: NSWindow! 15 | @IBOutlet weak var label: NSTextField! 16 | 17 | func applicationDidFinishLaunching(_ aNotification: Notification) { 18 | let bytes: [UInt8] = [1, 2, 3, 2, 1, 2] 19 | callbackExample { n in 20 | print(n) 21 | } 22 | var callbackResult: CInt? = nil 23 | contextCallbackExample { n in 24 | callbackResult = n 25 | } 26 | let m = Multiplier(5) 27 | label.stringValue = [ 28 | "5^2 = \(square(5))", 29 | "2 occurs \(count(byte: 2, in: bytes)) times in \(bytes)", 30 | "getSequence returned \(getSequence())", 31 | "Context callback returned \(callbackResult)", 32 | "5 * 3 = \(m.multiply(3))", 33 | ].joined(separator: "\n") 34 | } 35 | 36 | func applicationWillTerminate(_ aNotification: Notification) { 37 | } 38 | } 39 | 40 | 41 | // Swift to Haskell ByteString Example 42 | 43 | func count(byte: UInt8, in bytes: [UInt8]) -> Int { 44 | var r = 0 45 | bytes.withUnsafeBufferPointer { bytesBufPtr in 46 | r = Int(SwiftHaskell.countBytes(byte, 47 | HsPtr(mutating: bytesBufPtr.baseAddress), 48 | HsWord64(bytesBufPtr.count))) 49 | } 50 | return r 51 | } 52 | 53 | // Haskell to Swift ByteString Example 54 | 55 | func getSequence() -> [UInt8] { 56 | var n = 0 57 | let p = SwiftHaskell.getSequence(&n).assumingMemoryBound(to: UInt8.self) 58 | let a = [UInt8](UnsafeBufferPointer(start: p, count: n)) 59 | free(p) 60 | return a 61 | } 62 | 63 | // Swift to Haskell Callback Example 64 | 65 | func callbackExample(f: (@convention(c) (CInt) -> Void)) { 66 | let hsf = unsafeBitCast(f, to: HsFunPtr.self) 67 | SwiftHaskell.callbackExample(hsf) 68 | } 69 | 70 | // Swift to Haskell Callback with Context Example 71 | 72 | func contextCallbackExample(f: ((CInt) -> Void)) { 73 | class Wrap { 74 | var inner: T 75 | 76 | init(_ inner: T) { 77 | self.inner = inner 78 | } 79 | } 80 | let x = 3 81 | func release(context: HsPtr) { 82 | let _: Wrap<(CInt) -> Void> = Unmanaged.fromOpaque(context).takeRetainedValue() 83 | } 84 | func call(context: HsPtr, value: CInt) { 85 | let wf: Wrap<(CInt) -> Void> = Unmanaged.fromOpaque(context).takeUnretainedValue() 86 | let f = wf.inner 87 | f(value) 88 | } 89 | let release_hs = unsafeBitCast( 90 | release as @convention(c) (HsPtr) -> Void, to: HsFunPtr.self) 91 | let call_hs = unsafeBitCast( 92 | call as @convention(c) (HsPtr, CInt) -> Void, to: HsFunPtr.self) 93 | let ctx = Unmanaged.passRetained(Wrap(f)).toOpaque() 94 | SwiftHaskell.contextCallbackExample(ctx, release_hs, call_hs) 95 | } 96 | 97 | // Swift to Haskell Function Example 98 | 99 | class Multiplier { 100 | let funPtr: HsFunPtr 101 | 102 | init(_ x: CInt) { 103 | self.funPtr = SwiftHaskell.makeMultiplier(x) 104 | } 105 | 106 | func multiply(_ y: CInt) -> CInt { 107 | typealias F = @convention(c) (CInt) -> CInt 108 | let f = unsafeBitCast(self.funPtr, to: F.self) 109 | return f(y) 110 | } 111 | 112 | deinit { 113 | SwiftHaskell.freeMultiplier(self.funPtr) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Control.Concurrent (forkIO) 4 | 5 | import Data.ByteString (ByteString) 6 | import qualified Data.ByteString as B 7 | import qualified Data.ByteString.Char8 as B8 8 | import qualified Data.ByteString.Unsafe as BU 9 | import qualified Data.Text as T 10 | import Data.Word (Word8) 11 | 12 | import Foreign.C (CChar (CChar), CInt (CInt), CSize (CSize)) 13 | import Foreign.ForeignPtr (newForeignPtr, withForeignPtr) 14 | import Foreign.Marshal.Array (copyArray, mallocArray) 15 | import Foreign.Ptr (FunPtr, Ptr, freeHaskellFunPtr) 16 | import Foreign.Storable (poke) 17 | 18 | foreign export ccall square :: CInt -> CInt 19 | 20 | square :: CInt -> CInt 21 | square x = x * x 22 | 23 | foreign import ccall "runNSApplication" runNSApplication :: IO () 24 | 25 | main :: IO () 26 | main = do 27 | putStrLn "hello world" 28 | runNSApplication 29 | 30 | 31 | -- Examples 32 | 33 | -- Swift to Haskell ByteString Example 34 | 35 | foreign export ccall countBytes :: Word8 -> Ptr CChar -> CSize -> IO CSize 36 | 37 | countBytes :: Word8 -> Ptr CChar -> CSize -> IO CSize 38 | countBytes needle haystack haystackLen = do 39 | s <- B.packCStringLen (haystack, fromIntegral haystackLen) 40 | pure (B.foldl (\count b -> count + if b == needle then 1 else 0) 0 s) 41 | 42 | -- Haskell to Swift ByteString Example 43 | 44 | mallocCopyByteString :: ByteString -> IO (Ptr CChar, Int) 45 | mallocCopyByteString s = 46 | BU.unsafeUseAsCStringLen s $ \(p, n) -> do 47 | a <- mallocArray n 48 | copyArray a p n 49 | pure (a, n) 50 | 51 | foreign export ccall getSequence :: Ptr CSize -> IO (Ptr CChar) 52 | 53 | getSequence :: Ptr CSize -> IO (Ptr CChar) 54 | getSequence sizePtr = do 55 | (p, n) <- mallocCopyByteString (B.pack [1..10]) 56 | poke sizePtr (fromIntegral n) 57 | pure p 58 | 59 | -- Swift to Haskell Callback Example 60 | 61 | foreign export ccall callbackExample :: FunPtr (CInt -> IO ()) -> IO () 62 | foreign import ccall "dynamic" unwrapCallback :: FunPtr (CInt -> IO ()) -> (CInt -> IO ()) 63 | 64 | callbackExample :: FunPtr (CInt -> IO ()) -> IO () 65 | callbackExample f = (unwrapCallback f) 3 66 | 67 | -- Swift to Haskell Callback with Context Example 68 | 69 | foreign export ccall contextCallbackExample 70 | :: Ptr () 71 | -> FunPtr (Ptr () -> IO ()) 72 | -> FunPtr (Ptr () -> CInt -> IO ()) 73 | -> IO () 74 | foreign import ccall "dynamic" unwrapContextCallback 75 | :: FunPtr (Ptr () -> CInt -> IO ()) 76 | -> (Ptr () -> CInt -> IO ()) 77 | 78 | contextCallbackExample 79 | :: Ptr () -- ^ Context pointer 80 | -> FunPtr (Ptr () -> IO ()) -- ^ Context release function 81 | -> FunPtr (Ptr () -> CInt -> IO ()) -- ^ Callback function 82 | -> IO () 83 | contextCallbackExample ctxp releaseCtx callbackPtr = do 84 | ctxfp <- newForeignPtr releaseCtx ctxp 85 | let callback :: CInt -> IO () 86 | callback result = withForeignPtr ctxfp $ \ctxp' -> 87 | (unwrapContextCallback callbackPtr) ctxp' result 88 | _ <- forkIO $ do 89 | let result = 3 -- perform your complex computation here 90 | callback result 91 | pure () 92 | 93 | -- Swift to Haskell Function Example 94 | 95 | foreign export ccall makeMultiplier :: CInt -> IO (FunPtr (CInt -> CInt)) 96 | foreign import ccall "wrapper" wrapMultiplier 97 | :: (CInt -> CInt) 98 | -> IO (FunPtr (CInt -> CInt)) 99 | 100 | makeMultiplier :: CInt -> IO (FunPtr (CInt -> CInt)) 101 | makeMultiplier x = wrapMultiplier (x *) 102 | 103 | foreign export ccall freeMultiplier :: FunPtr (CInt -> CInt) -> IO () 104 | 105 | freeMultiplier :: FunPtr (CInt -> CInt) -> IO () 106 | freeMultiplier = freeHaskellFunPtr 107 | -------------------------------------------------------------------------------- /SwiftHaskell.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | BFABC4601E4C1DD1006036C6 /* SwiftAppLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = BFABC45E1E4C1DD1006036C6 /* SwiftAppLibrary.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | BFABC4CD1E4D627E006036C6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFABC4CC1E4D627E006036C6 /* AppDelegate.swift */; }; 12 | BFABC4D21E4D664D006036C6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4D01E4D664D006036C6 /* MainMenu.xib */; }; 13 | BFABC4E01E4D781D006036C6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4DF1E4D781D006036C6 /* Assets.xcassets */; }; 14 | BFABC4ED1E4D78E5006036C6 /* SwiftHaskell in Copy Files */ = {isa = PBXBuildFile; fileRef = BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 15 | BFABC4F01E4D7951006036C6 /* SwiftAppLibrary.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 16 | BFE4CC421E55553000F232D6 /* SwiftAppLibrary.m in Sources */ = {isa = PBXBuildFile; fileRef = BFE4CC411E55553000F232D6 /* SwiftAppLibrary.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | BFABC4E81E4D7842006036C6 /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = BFABC4521E4C1DD1006036C6 /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = BFABC45A1E4C1DD1006036C6; 25 | remoteInfo = SwiftAppLibrary; 26 | }; 27 | /* End PBXContainerItemProxy section */ 28 | 29 | /* Begin PBXCopyFilesBuildPhase section */ 30 | BFABC4EC1E4D78DF006036C6 /* Copy Files */ = { 31 | isa = PBXCopyFilesBuildPhase; 32 | buildActionMask = 2147483647; 33 | dstPath = ""; 34 | dstSubfolderSpec = 6; 35 | files = ( 36 | BFABC4ED1E4D78E5006036C6 /* SwiftHaskell in Copy Files */, 37 | ); 38 | name = "Copy Files"; 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | BFABC4EF1E4D7948006036C6 /* Embed Frameworks */ = { 42 | isa = PBXCopyFilesBuildPhase; 43 | buildActionMask = 2147483647; 44 | dstPath = ""; 45 | dstSubfolderSpec = 10; 46 | files = ( 47 | BFABC4F01E4D7951006036C6 /* SwiftAppLibrary.framework in Embed Frameworks */, 48 | ); 49 | name = "Embed Frameworks"; 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | /* End PBXCopyFilesBuildPhase section */ 53 | 54 | /* Begin PBXFileReference section */ 55 | BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftAppLibrary.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | BFABC45E1E4C1DD1006036C6 /* SwiftAppLibrary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftAppLibrary.h; sourceTree = ""; }; 57 | BFABC45F1E4C1DD1006036C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | BFABC4CC1E4D627E006036C6 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 59 | BFABC4D11E4D664D006036C6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = MainMenu.xib; sourceTree = ""; }; 60 | BFABC4D71E4D781D006036C6 /* SwiftHaskell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftHaskell.app; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | BFABC4DF1E4D781D006036C6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 62 | BFABC4E41E4D781D006036C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = SwiftHaskell; path = build/SwiftHaskell; sourceTree = SOURCE_ROOT; }; 64 | BFE4CC411E55553000F232D6 /* SwiftAppLibrary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwiftAppLibrary.m; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | BFABC4571E4C1DD1006036C6 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | BFABC4511E4C1DD1006036C6 = { 79 | isa = PBXGroup; 80 | children = ( 81 | BFABC4D81E4D781D006036C6 /* SwiftHaskell */, 82 | BFABC45D1E4C1DD1006036C6 /* SwiftAppLibrary */, 83 | BFABC45C1E4C1DD1006036C6 /* Products */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | BFABC45C1E4C1DD1006036C6 /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */, 91 | BFABC4D71E4D781D006036C6 /* SwiftHaskell.app */, 92 | ); 93 | name = Products; 94 | sourceTree = ""; 95 | }; 96 | BFABC45D1E4C1DD1006036C6 /* SwiftAppLibrary */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | BFABC45E1E4C1DD1006036C6 /* SwiftAppLibrary.h */, 100 | BFE4CC411E55553000F232D6 /* SwiftAppLibrary.m */, 101 | BFABC4CC1E4D627E006036C6 /* AppDelegate.swift */, 102 | BFABC4D01E4D664D006036C6 /* MainMenu.xib */, 103 | BFABC45F1E4C1DD1006036C6 /* Info.plist */, 104 | ); 105 | path = SwiftAppLibrary; 106 | sourceTree = ""; 107 | }; 108 | BFABC4D81E4D781D006036C6 /* SwiftHaskell */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | BFABC4DF1E4D781D006036C6 /* Assets.xcassets */, 112 | BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */, 113 | BFABC4E41E4D781D006036C6 /* Info.plist */, 114 | ); 115 | path = SwiftHaskell; 116 | sourceTree = ""; 117 | }; 118 | /* End PBXGroup section */ 119 | 120 | /* Begin PBXHeadersBuildPhase section */ 121 | BFABC4581E4C1DD1006036C6 /* Headers */ = { 122 | isa = PBXHeadersBuildPhase; 123 | buildActionMask = 2147483647; 124 | files = ( 125 | BFABC4601E4C1DD1006036C6 /* SwiftAppLibrary.h in Headers */, 126 | ); 127 | runOnlyForDeploymentPostprocessing = 0; 128 | }; 129 | /* End PBXHeadersBuildPhase section */ 130 | 131 | /* Begin PBXNativeTarget section */ 132 | BFABC45A1E4C1DD1006036C6 /* SwiftAppLibrary */ = { 133 | isa = PBXNativeTarget; 134 | buildConfigurationList = BFABC4631E4C1DD1006036C6 /* Build configuration list for PBXNativeTarget "SwiftAppLibrary" */; 135 | buildPhases = ( 136 | BF2EA5CA1E5030E100651018 /* stack build and link-deps */, 137 | BFABC4561E4C1DD1006036C6 /* Sources */, 138 | BFABC4571E4C1DD1006036C6 /* Frameworks */, 139 | BFABC4581E4C1DD1006036C6 /* Headers */, 140 | BFABC4591E4C1DD1006036C6 /* Resources */, 141 | BFABC4CE1E4D6344006036C6 /* Link framework to build */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = SwiftAppLibrary; 148 | productName = SwiftAppLibrary; 149 | productReference = BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */; 150 | productType = "com.apple.product-type.framework"; 151 | }; 152 | BFABC4D61E4D781D006036C6 /* SwiftHaskell */ = { 153 | isa = PBXNativeTarget; 154 | buildConfigurationList = BFABC4E51E4D781D006036C6 /* Build configuration list for PBXNativeTarget "SwiftHaskell" */; 155 | buildPhases = ( 156 | BFABC4D51E4D781D006036C6 /* Resources */, 157 | BFABC4EF1E4D7948006036C6 /* Embed Frameworks */, 158 | BFABC4EC1E4D78DF006036C6 /* Copy Files */, 159 | ); 160 | buildRules = ( 161 | ); 162 | dependencies = ( 163 | BFABC4E91E4D7842006036C6 /* PBXTargetDependency */, 164 | ); 165 | name = SwiftHaskell; 166 | productName = SwiftHaskell; 167 | productReference = BFABC4D71E4D781D006036C6 /* SwiftHaskell.app */; 168 | productType = "com.apple.product-type.application"; 169 | }; 170 | /* End PBXNativeTarget section */ 171 | 172 | /* Begin PBXProject section */ 173 | BFABC4521E4C1DD1006036C6 /* Project object */ = { 174 | isa = PBXProject; 175 | attributes = { 176 | LastUpgradeCheck = 0820; 177 | ORGANIZATIONNAME = nanotech; 178 | TargetAttributes = { 179 | BFABC45A1E4C1DD1006036C6 = { 180 | CreatedOnToolsVersion = 8.2; 181 | LastSwiftMigration = 0820; 182 | ProvisioningStyle = Automatic; 183 | }; 184 | BFABC4D61E4D781D006036C6 = { 185 | CreatedOnToolsVersion = 8.2; 186 | ProvisioningStyle = Automatic; 187 | }; 188 | }; 189 | }; 190 | buildConfigurationList = BFABC4551E4C1DD1006036C6 /* Build configuration list for PBXProject "SwiftHaskell" */; 191 | compatibilityVersion = "Xcode 3.2"; 192 | developmentRegion = English; 193 | hasScannedForEncodings = 0; 194 | knownRegions = ( 195 | en, 196 | Base, 197 | ); 198 | mainGroup = BFABC4511E4C1DD1006036C6; 199 | productRefGroup = BFABC45C1E4C1DD1006036C6 /* Products */; 200 | projectDirPath = ""; 201 | projectRoot = ""; 202 | targets = ( 203 | BFABC4D61E4D781D006036C6 /* SwiftHaskell */, 204 | BFABC45A1E4C1DD1006036C6 /* SwiftAppLibrary */, 205 | ); 206 | }; 207 | /* End PBXProject section */ 208 | 209 | /* Begin PBXResourcesBuildPhase section */ 210 | BFABC4591E4C1DD1006036C6 /* Resources */ = { 211 | isa = PBXResourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | BFABC4D21E4D664D006036C6 /* MainMenu.xib in Resources */, 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | BFABC4D51E4D781D006036C6 /* Resources */ = { 219 | isa = PBXResourcesBuildPhase; 220 | buildActionMask = 2147483647; 221 | files = ( 222 | BFABC4E01E4D781D006036C6 /* Assets.xcassets in Resources */, 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | /* End PBXResourcesBuildPhase section */ 227 | 228 | /* Begin PBXShellScriptBuildPhase section */ 229 | BF2EA5CA1E5030E100651018 /* stack build and link-deps */ = { 230 | isa = PBXShellScriptBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | ); 234 | inputPaths = ( 235 | "$(PROJECT_DIR)/src/Main.hs", 236 | "$(PROJECT_DIR)/SwiftHaskellLibrary.cabal", 237 | "$(PROJECT_DIR)/stack.yaml", 238 | ); 239 | name = "stack build and link-deps"; 240 | outputPaths = ( 241 | "$(PROJECT_DIR)/build/SwiftHaskell", 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | shellPath = /bin/sh; 245 | shellScript = "stack build\nbash link-deps.sh"; 246 | showEnvVarsInLog = 0; 247 | }; 248 | BFABC4CE1E4D6344006036C6 /* Link framework to build */ = { 249 | isa = PBXShellScriptBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | ); 253 | inputPaths = ( 254 | "$(BUILT_PRODUCTS_DIR)/$(FULL_PRODUCT_NAME)", 255 | ); 256 | name = "Link framework to build"; 257 | outputPaths = ( 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | shellPath = /bin/sh; 261 | shellScript = "set -u\nln -sf \"${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}\" \"${PROJECT_DIR}/build/\""; 262 | showEnvVarsInLog = 0; 263 | }; 264 | /* End PBXShellScriptBuildPhase section */ 265 | 266 | /* Begin PBXSourcesBuildPhase section */ 267 | BFABC4561E4C1DD1006036C6 /* Sources */ = { 268 | isa = PBXSourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | BFABC4CD1E4D627E006036C6 /* AppDelegate.swift in Sources */, 272 | BFE4CC421E55553000F232D6 /* SwiftAppLibrary.m in Sources */, 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | /* End PBXSourcesBuildPhase section */ 277 | 278 | /* Begin PBXTargetDependency section */ 279 | BFABC4E91E4D7842006036C6 /* PBXTargetDependency */ = { 280 | isa = PBXTargetDependency; 281 | target = BFABC45A1E4C1DD1006036C6 /* SwiftAppLibrary */; 282 | targetProxy = BFABC4E81E4D7842006036C6 /* PBXContainerItemProxy */; 283 | }; 284 | /* End PBXTargetDependency section */ 285 | 286 | /* Begin PBXVariantGroup section */ 287 | BFABC4D01E4D664D006036C6 /* MainMenu.xib */ = { 288 | isa = PBXVariantGroup; 289 | children = ( 290 | BFABC4D11E4D664D006036C6 /* Base */, 291 | ); 292 | name = MainMenu.xib; 293 | path = Base.lproj; 294 | sourceTree = ""; 295 | }; 296 | /* End PBXVariantGroup section */ 297 | 298 | /* Begin XCBuildConfiguration section */ 299 | BFABC4611E4C1DD1006036C6 /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | ALWAYS_SEARCH_USER_PATHS = NO; 303 | CLANG_ANALYZER_NONNULL = YES; 304 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 305 | CLANG_CXX_LIBRARY = "libc++"; 306 | CLANG_ENABLE_MODULES = YES; 307 | CLANG_ENABLE_OBJC_ARC = YES; 308 | CLANG_WARN_BOOL_CONVERSION = YES; 309 | CLANG_WARN_CONSTANT_CONVERSION = YES; 310 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 311 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 312 | CLANG_WARN_EMPTY_BODY = YES; 313 | CLANG_WARN_ENUM_CONVERSION = YES; 314 | CLANG_WARN_INFINITE_RECURSION = YES; 315 | CLANG_WARN_INT_CONVERSION = YES; 316 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 317 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 318 | CLANG_WARN_UNREACHABLE_CODE = YES; 319 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 320 | CODE_SIGN_IDENTITY = "-"; 321 | COPY_PHASE_STRIP = NO; 322 | CURRENT_PROJECT_VERSION = 1; 323 | DEBUG_INFORMATION_FORMAT = dwarf; 324 | ENABLE_STRICT_OBJC_MSGSEND = YES; 325 | ENABLE_TESTABILITY = YES; 326 | GCC_C_LANGUAGE_STANDARD = gnu99; 327 | GCC_DYNAMIC_NO_PIC = NO; 328 | GCC_NO_COMMON_BLOCKS = YES; 329 | GCC_OPTIMIZATION_LEVEL = 0; 330 | GCC_PREPROCESSOR_DEFINITIONS = ( 331 | "DEBUG=1", 332 | "$(inherited)", 333 | ); 334 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 335 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 336 | GCC_WARN_UNDECLARED_SELECTOR = YES; 337 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 338 | GCC_WARN_UNUSED_FUNCTION = YES; 339 | GCC_WARN_UNUSED_VARIABLE = YES; 340 | MACOSX_DEPLOYMENT_TARGET = 10.11; 341 | MTL_ENABLE_DEBUG_INFO = YES; 342 | ONLY_ACTIVE_ARCH = YES; 343 | SDKROOT = macosx; 344 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 345 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 346 | VERSIONING_SYSTEM = "apple-generic"; 347 | VERSION_INFO_PREFIX = ""; 348 | }; 349 | name = Debug; 350 | }; 351 | BFABC4621E4C1DD1006036C6 /* Release */ = { 352 | isa = XCBuildConfiguration; 353 | buildSettings = { 354 | ALWAYS_SEARCH_USER_PATHS = NO; 355 | CLANG_ANALYZER_NONNULL = YES; 356 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 357 | CLANG_CXX_LIBRARY = "libc++"; 358 | CLANG_ENABLE_MODULES = YES; 359 | CLANG_ENABLE_OBJC_ARC = YES; 360 | CLANG_WARN_BOOL_CONVERSION = YES; 361 | CLANG_WARN_CONSTANT_CONVERSION = YES; 362 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 363 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 364 | CLANG_WARN_EMPTY_BODY = YES; 365 | CLANG_WARN_ENUM_CONVERSION = YES; 366 | CLANG_WARN_INFINITE_RECURSION = YES; 367 | CLANG_WARN_INT_CONVERSION = YES; 368 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 369 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 370 | CLANG_WARN_UNREACHABLE_CODE = YES; 371 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 372 | CODE_SIGN_IDENTITY = "-"; 373 | COPY_PHASE_STRIP = NO; 374 | CURRENT_PROJECT_VERSION = 1; 375 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 376 | ENABLE_NS_ASSERTIONS = NO; 377 | ENABLE_STRICT_OBJC_MSGSEND = YES; 378 | GCC_C_LANGUAGE_STANDARD = gnu99; 379 | GCC_NO_COMMON_BLOCKS = YES; 380 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 381 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 382 | GCC_WARN_UNDECLARED_SELECTOR = YES; 383 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 384 | GCC_WARN_UNUSED_FUNCTION = YES; 385 | GCC_WARN_UNUSED_VARIABLE = YES; 386 | MACOSX_DEPLOYMENT_TARGET = 10.11; 387 | MTL_ENABLE_DEBUG_INFO = NO; 388 | SDKROOT = macosx; 389 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 390 | VERSIONING_SYSTEM = "apple-generic"; 391 | VERSION_INFO_PREFIX = ""; 392 | }; 393 | name = Release; 394 | }; 395 | BFABC4641E4C1DD1006036C6 /* Debug */ = { 396 | isa = XCBuildConfiguration; 397 | buildSettings = { 398 | CLANG_ENABLE_MODULES = YES; 399 | CODE_SIGN_IDENTITY = ""; 400 | COMBINE_HIDPI_IMAGES = YES; 401 | DEFINES_MODULE = YES; 402 | DYLIB_COMPATIBILITY_VERSION = 1; 403 | DYLIB_CURRENT_VERSION = 1; 404 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 405 | FRAMEWORK_VERSION = A; 406 | INFOPLIST_FILE = SwiftAppLibrary/Info.plist; 407 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 408 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 409 | OTHER_LDFLAGS = ( 410 | "-undefined", 411 | dynamic_lookup, 412 | ); 413 | PRODUCT_BUNDLE_IDENTIFIER = net.nanotechcorp.SwiftAppLibrary; 414 | PRODUCT_NAME = "$(TARGET_NAME)"; 415 | SKIP_INSTALL = YES; 416 | SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/SwiftHaskell/include"; 417 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 418 | SWIFT_VERSION = 3.0; 419 | USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/build/ghc/include"; 420 | }; 421 | name = Debug; 422 | }; 423 | BFABC4651E4C1DD1006036C6 /* Release */ = { 424 | isa = XCBuildConfiguration; 425 | buildSettings = { 426 | CLANG_ENABLE_MODULES = YES; 427 | CODE_SIGN_IDENTITY = ""; 428 | COMBINE_HIDPI_IMAGES = YES; 429 | DEFINES_MODULE = YES; 430 | DYLIB_COMPATIBILITY_VERSION = 1; 431 | DYLIB_CURRENT_VERSION = 1; 432 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 433 | FRAMEWORK_VERSION = A; 434 | INFOPLIST_FILE = SwiftAppLibrary/Info.plist; 435 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 437 | OTHER_LDFLAGS = ( 438 | "-undefined", 439 | dynamic_lookup, 440 | ); 441 | PRODUCT_BUNDLE_IDENTIFIER = net.nanotechcorp.SwiftAppLibrary; 442 | PRODUCT_NAME = "$(TARGET_NAME)"; 443 | SKIP_INSTALL = YES; 444 | SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/SwiftHaskell/include"; 445 | SWIFT_VERSION = 3.0; 446 | USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/build/ghc/include"; 447 | }; 448 | name = Release; 449 | }; 450 | BFABC4E61E4D781D006036C6 /* Debug */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 454 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 455 | COMBINE_HIDPI_IMAGES = YES; 456 | INFOPLIST_FILE = SwiftHaskell/Info.plist; 457 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 458 | PRODUCT_BUNDLE_IDENTIFIER = net.nanotechcorp.SwiftHaskell; 459 | PRODUCT_NAME = "$(TARGET_NAME)"; 460 | }; 461 | name = Debug; 462 | }; 463 | BFABC4E71E4D781D006036C6 /* Release */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 467 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 468 | COMBINE_HIDPI_IMAGES = YES; 469 | INFOPLIST_FILE = SwiftHaskell/Info.plist; 470 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 471 | PRODUCT_BUNDLE_IDENTIFIER = net.nanotechcorp.SwiftHaskell; 472 | PRODUCT_NAME = "$(TARGET_NAME)"; 473 | }; 474 | name = Release; 475 | }; 476 | /* End XCBuildConfiguration section */ 477 | 478 | /* Begin XCConfigurationList section */ 479 | BFABC4551E4C1DD1006036C6 /* Build configuration list for PBXProject "SwiftHaskell" */ = { 480 | isa = XCConfigurationList; 481 | buildConfigurations = ( 482 | BFABC4611E4C1DD1006036C6 /* Debug */, 483 | BFABC4621E4C1DD1006036C6 /* Release */, 484 | ); 485 | defaultConfigurationIsVisible = 0; 486 | defaultConfigurationName = Release; 487 | }; 488 | BFABC4631E4C1DD1006036C6 /* Build configuration list for PBXNativeTarget "SwiftAppLibrary" */ = { 489 | isa = XCConfigurationList; 490 | buildConfigurations = ( 491 | BFABC4641E4C1DD1006036C6 /* Debug */, 492 | BFABC4651E4C1DD1006036C6 /* Release */, 493 | ); 494 | defaultConfigurationIsVisible = 0; 495 | defaultConfigurationName = Release; 496 | }; 497 | BFABC4E51E4D781D006036C6 /* Build configuration list for PBXNativeTarget "SwiftHaskell" */ = { 498 | isa = XCConfigurationList; 499 | buildConfigurations = ( 500 | BFABC4E61E4D781D006036C6 /* Debug */, 501 | BFABC4E71E4D781D006036C6 /* Release */, 502 | ); 503 | defaultConfigurationIsVisible = 0; 504 | defaultConfigurationName = Release; 505 | }; 506 | /* End XCConfigurationList section */ 507 | }; 508 | rootObject = BFABC4521E4C1DD1006036C6 /* Project object */; 509 | } 510 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Integrating Haskell with Swift Mac Apps 2 | 3 | In this tutorial, we'll create a Mac app using Swift and Xcode 4 | to build the UI, and Haskell to implement backend logic. 5 | 6 | A basic familiarity with Haskell, Swift, Objective-C, C, Xcode, 7 | and shell scripting will be assumed. 8 | 9 | ### Table of Contents 10 | 11 | 1. [Project Setup](#project-setup) 12 | 2. [Exporting Haskell Functions](#exporting-haskell-functions) 13 | 3. [Importing Haskell's Generated FFI Headers into Swift](#importing-haskells-generated-ffi-headers-into-swift) 14 | 4. [Converting the Swift App to a Framework](#converting-the-swift-app-to-a-framework) 15 | 1. [Framework Configuration](#framework-configuration) 16 | 2. [App Bundle Configuration](#app-bundle-configuration) 17 | 5. [Linking to the Framework](#linking-to-the-framework) 18 | 6. [Starting Cocoa](#starting-cocoa) 19 | 7. [Linking to the Executable](#linking-to-the-executable) 20 | 8. [Calling Haskell from Swift](#calling-haskell-from-swift) 21 | 9. [Passing Complex Data Types](#passing-complex-data-types) 22 | 1. [Bytes](#bytes) 23 | 2. [Functions and Closures](#functions-and-closures) 24 | 10. [Troubleshooting](#troubleshooting) 25 | 26 | ## Project Setup 27 | 28 | To start, create a new Cocoa Application Xcode project 29 | 30 | ![Select the Cocoa app template](tutorial/xcode-cocoa-template-icon.png) 31 | 32 | with Swift as the default language. 33 | 34 | ![Select Swift as the default language](tutorial/xcode-create-project-language-swift.png) 35 | 36 | Name the project SwiftHaskell. 37 | 38 | `cd` into the directory with the `.xcodeproj` and create a new 39 | stack project: 40 | 41 | ```sh 42 | $ cd SwiftHaskell 43 | $ stack new SwiftHaskellLibrary simple 44 | ``` 45 | 46 | Move these files up to the top directory, so we can run all of 47 | our commands from the same directory: 48 | 49 | ```sh 50 | $ mv -vn SwiftHaskellLibrary/* . 51 | $ rmdir SwiftHaskellLibrary 52 | ``` 53 | 54 | In `SwiftHaskellLibrary.cabal`, rename the executable to 55 | match the Xcode app's name of SwiftHaskell: 56 | 57 | ```cabal 58 | executable SwiftHaskell 59 | ``` 60 | 61 | To combine our Haskell library with our Swift UI, we'll build 62 | the Swift app as a framework and link to it from the Haskell 63 | executable. Xcode will then package both up into an app bundle. 64 | 65 | The reason for doing the linking in this direction is that 66 | building a self-contained dynamic library is currently simpler 67 | with Swift and Xcode than it is with Cabal. 68 | 69 | ## Exporting Haskell Functions 70 | 71 | Here's the trivial function `square` that we'll export as a 72 | simple first example: 73 | 74 | ```haskell 75 | square x = x * x 76 | ``` 77 | 78 | Haskell functions exported via the FFI can only contain 79 | certain types in their signatures that are compatible with C: 80 | primitive integers, floats and doubles, and pointer types. 81 | The full list is in [section 8.7 of the Haskell 82 | Report][haskell-report-8.7]. 83 | 84 | Since we'll only be using `square` to demonstrate the FFI, let's 85 | assign it a FFI-compatible type directly. For more complex 86 | functions, wrap them in a new function and convert their inputs 87 | and outputs as needed. 88 | 89 | ```haskell 90 | import Foreign.C 91 | 92 | square :: CInt -> CInt 93 | square x = x * x 94 | ``` 95 | 96 | To export `square`, add a `foreign export` definition with a 97 | calling convention of `ccall`: 98 | 99 | ```haskell 100 | foreign export ccall square :: CInt -> CInt 101 | ``` 102 | 103 | For the full syntax of `foreign export`, see [section 8.3 of the 104 | Haskell Report][haskell-report-8.3]. 105 | 106 | [haskell-report-8.7]: https://www.haskell.org/onlinereport/haskell2010/haskellch8.html#x15-1700008.7 107 | [haskell-report-8.3]: https://www.haskell.org/onlinereport/haskell2010/haskellch8.html#x15-1530008.3 108 | 109 | Together, `src/Main.hs` is 110 | 111 | ```haskell 112 | module Main where 113 | 114 | import Foreign.C 115 | 116 | foreign export ccall square :: CInt -> CInt 117 | 118 | square :: CInt -> CInt 119 | square x = x * x 120 | 121 | main :: IO () 122 | main = do 123 | putStrLn "hello world" 124 | ``` 125 | 126 | ## Importing Haskell's Generated FFI Headers into Swift 127 | 128 | If we now `stack build`, in addition to building the library, 129 | GHC will generate C header files for each module with foreign 130 | exports. Because these are build artifacts, they're buried 131 | somewhat deep in the file hierarchy, but we can ask `stack` 132 | and `find` where they are: 133 | 134 | $ find "$(stack path --dist-dir)" -name Main_stub.h 135 | .stack-work/dist/x86_64-osx/Cabal-1.24.0.0/build/SwiftHaskellLibrary/SwiftHaskellLibrary-tmp/Main_stub.h 136 | 137 | These stub headers `#include "HsFFI.h"` from GHC, so we'll also 138 | need to find the current compiler's version of that header. 139 | 140 | $ find "$(stack path --compiler-bin)/.." -name HsFFI.h 141 | /Users/nanotech/.stack/programs/x86_64-osx/ghc-8.0.1/bin/../lib/ghc-8.0.1/include/HsFFI.h 142 | 143 | Since we'll be importing these headers into a Swift framework, 144 | we won't be able to use `#include` as we would in C. Instead, 145 | Swift uses [Clang's module format][clang-modules]. (Swift 146 | applications can use [bridging headers][swift-bridging-headers], 147 | but frameworks [must use modules][so-non-modular-header].) A 148 | `module.modulemap` file to import `Main_stub.h` looks like 149 | 150 | module SwiftHaskell { 151 | header "Main_stub.h" 152 | export * 153 | } 154 | 155 | [swift-bridging-headers]: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-ID156 156 | [so-non-modular-header]: https://stackoverflow.com/questions/24103169/swift-compiler-error-non-modular-header-inside-framework-module/37072619#37072619 157 | [clang-modules]: http://clang.llvm.org/docs/Modules.html 158 | 159 | As the paths to these headers vary, let's use a script to 160 | automatically copy them out and build a module map. We'll also 161 | create a symlink to the built executable's location for later. 162 | 163 | ```bash 164 | #!/usr/bin/env bash 165 | set -eu 166 | 167 | # Change EXECUTABLE_NAME to the name of your Haskell executable. 168 | EXECUTABLE_NAME=SwiftHaskell 169 | 170 | DIST_DIR="$(stack path --dist-dir)" 171 | GHC_VERSION="$(stack exec -- ghc --numeric-version)" 172 | GHC_LIB_DIR="$(stack path --compiler-bin)/../lib/ghc-$GHC_VERSION" 173 | STUB_BUILD_DIR="${DIST_DIR}/build/${EXECUTABLE_NAME}/${EXECUTABLE_NAME}-tmp" 174 | STUB_MODULE_DIR="${EXECUTABLE_NAME}/include" 175 | STUB_MODULE_MAP="${STUB_MODULE_DIR}/module.modulemap" 176 | 177 | # Create a module map from the generated Haskell 178 | # FFI export headers for importing into Swift. 179 | mkdir -p "${STUB_MODULE_DIR}" 180 | NL=" 181 | " 182 | module_map="module ${EXECUTABLE_NAME} {${NL}" 183 | for h in $(find "${STUB_BUILD_DIR}" -name '*.h'); do 184 | h_filename="${h/$STUB_BUILD_DIR\//}" 185 | cp "$h" "${STUB_MODULE_DIR}/" 186 | module_map="${module_map} header \"${h_filename}\"${NL}" 187 | done 188 | module_map="${module_map} export *${NL}" 189 | module_map="${module_map}}" 190 | echo "${module_map}" > "${STUB_MODULE_MAP}" 191 | 192 | # Symlink to the current GHC's header directory so we can add it 193 | # to Xcode's include path as $(PROJECT_DIR)/build/ghc/include. 194 | mkdir -p build/ghc 195 | ln -sf "${GHC_LIB_DIR}/include" build/ghc/ 196 | 197 | # Symlink to the Haskell executable so we can easily drag it 198 | # into Xcode to use as the executable for the app bundle. 199 | ln -sf "../${DIST_DIR}/build/${EXECUTABLE_NAME}/${EXECUTABLE_NAME}" build/ 200 | ``` 201 | 202 | Change the value of the `EXECUTABLE_NAME` variable to the 203 | name of the executable in your `.cabal` file if you named it 204 | something other than SwiftHaskell. 205 | 206 | Save the script as `link-deps.sh`, run `stack build`, and then 207 | run `bash link-deps.sh` to prepare for the next section. 208 | 209 | Running the script will create 210 | `SwiftHaskell/include/module.modulemap` and two symlinks in 211 | the project's `build/` directory: 212 | 213 | - `SwiftHaskell` `-> ../.stack-work/dist/{arch}/Cabal-{version}/build/SwiftHaskell/SwiftHaskell` 214 | - `ghc` 215 | - `include` `-> ~/.stack/programs/{arch}/ghc-{version}/bin/../lib/ghc-{version}/include` 216 | 217 | Text marked as `{}` will vary. 218 | 219 | ## Converting the Swift App to a Framework 220 | 221 | Create a new Cocoa Framework target in the Xcode project, 222 | 223 | ![Select the Cocoa Framework template](tutorial/xcode-cocoa-framework-template-icon.png) 224 | 225 | with Swift as the default language. 226 | 227 | ![Select Swift as the default language](tutorial/xcode-create-project-language-swift.png) 228 | 229 | Name the framework SwiftAppLibrary. 230 | 231 | ### Framework Configuration 232 | 233 | To move over our application code and UI, change the Target 234 | Membership of `AppDelegate.swift` and `MainMenu.xib` in Xcode's 235 | File Inspector in the right sidebar so that they are only 236 | included in SwiftAppLibrary: 237 | 238 | ![Target Membership](tutorial/xcode-target-membership.png) 239 | 240 | In `AppDelegate.swift`, remove the `@NSApplicationMain` 241 | attribute from the `AppDelegate` class, as we don't want an 242 | auto-generated `main` function in our framework. We will 243 | implement an equivalent way to start Cocoa later, to be called 244 | from the Haskell executable's `main`. 245 | 246 | Xcode will place the built framework in a temporary directory 247 | (`~/Library/Developer/Xcode/DerivedData/`) with an unpredictable 248 | subpath. So that Cabal will be able to find the framework for 249 | linking, add a new **Run Script** build phase 250 | 251 | ![New Run Script Phase](tutorial/xcode-new-framework-run-script-phase.png) 252 | 253 | that creates a symlink to the built framework in `build/`: 254 | 255 | ```sh 256 | set -u 257 | ln -sf "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}" "${PROJECT_DIR}/build/" 258 | ``` 259 | 260 | ### App Bundle Configuration 261 | 262 | Drag the `SwiftHaskell` executable we built previously with 263 | Stack into Xcode from the `build/` directory that we symlinked 264 | it into, 265 | 266 | ![The SwiftHaskell executable in Xcode](tutorial/xcode-drag-swifthaskell-executable.png) 267 | 268 | but do not add it to any targets when prompted: 269 | 270 | ![Do not add the executable to any targets](tutorial/xcode-add-to-no-targets.png) 271 | 272 | When Xcode creates Swift frameworks, it expects that the 273 | application that links the framework will include the Swift 274 | standard libraries. Xcode automatically adds these libraries to 275 | Swift applications. Since our application executable is built 276 | with Haskell and not Swift, we'll need to explicitly tell Xcode 277 | to include the Swift standard libraries in our application. 278 | 279 | In the app target's Build Settings tab, set **Always Embed Swift 280 | Standard Libraries** to **Yes**: 281 | 282 | ![Always Embed Swift Standard Libraries: Yes](tutorial/xcode-embed-swift-standard-libs.png) 283 | 284 | In the app target's Build Phases, remove the **Compile Sources** 285 | and **Link Binary With Libraries** phases. We are using Stack to 286 | build the app's executable instead of Xcode. 287 | 288 | Add a new **Copy Files** phase that copies the `SwiftHaskell` 289 | executable into the app bundle's Executables directory: 290 | 291 | ![New Copy Files Phase](tutorial/xcode-new-copy-files-phase.png) 292 | 293 | ![Select the executable](tutorial/xcode-choose-executable.png) 294 | 295 | ![Copy into Executables](tutorial/xcode-copy-files-swifthaskell-executable.png) 296 | 297 | Build the SwiftAppLibrary framework in Xcode to prepare for the 298 | next sections. 299 | 300 | ![Select the SwiftAppLibrary target](tutorial/xcode-select-framework-target.png) 301 | 302 | ## Linking to the Framework 303 | 304 | Add these options to the executable's section in the `.cabal` 305 | file: 306 | 307 | ```cabal 308 | executable SwiftHaskell 309 | ghc-options: -threaded -framework-path build 310 | ld-options: -rpath @executable_path/../Frameworks 311 | frameworks: SwiftAppLibrary 312 | ``` 313 | 314 | - `-threaded` enables the multithreaded GHC runtime, which is 315 | usually what you want. 316 | - `-framework-path build` tells GHC to look for frameworks where we 317 | symlinked our framework to. 318 | - `-rpath @executable_path/../Frameworks` embeds into the 319 | executable where the dynamic linker should look for shared 320 | libraries. 321 | 322 | See [the Flag Reference in the GHC Users' 323 | Guide][ghc-guide-flags] and [`man 1 ld`][man-ld] for more details. 324 | 325 | [ghc-guide-flags]: https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/flags.html 326 | [man-ld]: x-man-page://1/ld 327 | 328 | ## Starting Cocoa 329 | 330 | Because Haskell has control over the program's entry point 331 | (`main`), we'll need to have it call out to Cocoa to start its 332 | main thread. In `SwiftAppLibrary.h`, declare a new function 333 | named `runNSApplication` and mark it as `FOUNDATION_EXPORT` to 334 | indicate that it should be exported from the framework: 335 | 336 | ```c 337 | FOUNDATION_EXPORT void runNSApplication(void); 338 | ``` 339 | 340 | Implement the function by adding a new Objective-C `.m` file to 341 | the framework target containing 342 | 343 | ```objective-c 344 | #import "SwiftAppLibrary.h" 345 | 346 | @interface AClassInThisFramework : NSObject @end 347 | @implementation AClassInThisFramework @end 348 | 349 | void runNSApplication(void) { 350 | NSApplication *app = [NSApplication sharedApplication]; 351 | NSBundle *bundle = [NSBundle bundleForClass:[AClassInThisFramework class]]; 352 | NSArray *topObjects; 353 | [[[NSNib alloc] initWithNibNamed:@"MainMenu" bundle:bundle] 354 | instantiateWithOwner:app topLevelObjects:&topObjects]; 355 | [app run]; 356 | } 357 | ``` 358 | 359 | This *is* possible to write in Swift, however as of Swift 3.0.2, 360 | the annotation to export unmangled C symbols (`@_cdecl`) is not 361 | documented as stable. Additionally, whole module optimization 362 | will assume that `@_cdecl` symbols are unused and remove them. 363 | 364 | In `Main.hs`, import the foreign function and call it from 365 | the end of `main`: 366 | 367 | ```haskell 368 | module Main where 369 | 370 | import Foreign.C 371 | 372 | foreign export ccall square :: CInt -> CInt 373 | 374 | square :: CInt -> CInt 375 | square x = x * x 376 | 377 | foreign import ccall "runNSApplication" runNSApplication :: IO () 378 | 379 | main :: IO () 380 | main = do 381 | putStrLn "hello world" 382 | runNSApplication 383 | ``` 384 | 385 | `runNSApplication` will not return, being busy with Cocoa's 386 | main run loop. Use `Control.Concurrent.forkIO` before calling 387 | `runNSApplication` to run other tasks as needed. 388 | 389 | Build the `SwiftHaskellLibrary` framework in Xcode, then `stack 390 | build`, and then finally build and run the `SwiftHaskell` 391 | app target to launch the app and see the default window from 392 | `MainMenu.xib`: 393 | 394 | ![A blank window](tutorial/empty-app.png) 395 | 396 | ## Linking to the Executable 397 | 398 | To tell Xcode where to find our `module.modulemap`, add 399 | `$(PROJECT_DIR)/SwiftHaskell/include` to the framework target's 400 | **Swift Compiler - Search Paths, Import Paths** setting in 401 | Xcode, 402 | 403 | ![The Swift module import paths](tutorial/xcode-swift-module-search-paths.png) 404 | 405 | and, for the GHC headers the module depends on, add 406 | `$(PROJECT_DIR)/build/ghc/include` to the framework's **User 407 | Header Search Paths** setting: 408 | 409 | ![The User Header Search Paths](tutorial/xcode-header-search-paths.png) 410 | 411 | In order for the framework to be able to link to symbols in the 412 | Haskell executable, we need to tell the linker to leave symbols 413 | undefined and have them be resolved at runtime. 414 | 415 | Add [`-undefined dynamic_lookup`][man-ld] to the framework's 416 | **Other Linker Flags** setting. 417 | 418 | Be aware that this means that link errors will occur at runtime 419 | instead of at link time. Also note that the framework linking 420 | to symbols in the executable (and depending on the generated 421 | headers), and the executable linking to the framework, creates 422 | a circular dependency. When building the project clean, you 423 | will need to build the components in this order: 424 | 425 | - `stack build` to generate the Haskell FFI export headers. 426 | Linking will fail, as the Swift framework is not built yet. 427 | - Build the Swift framework. 428 | - `stack build` 429 | - Build the app bundle. 430 | 431 | The first step can be skipped subsequently by committing the 432 | generated headers to source control. 433 | 434 | To help automate this, add a new **Run Script** build phase to 435 | the beginning of the framework's build phases with the contents 436 | 437 | ```sh 438 | stack build 439 | bash link-deps.sh 440 | ``` 441 | 442 | Add the Haskell sources as input files: 443 | 444 | ``` 445 | $(PROJECT_DIR)/src/Main.hs 446 | $(PROJECT_DIR)/SwiftHaskellLibrary.cabal 447 | $(PROJECT_DIR)/stack.yaml 448 | ``` 449 | 450 | And the executable as an output file: 451 | 452 | ``` 453 | $(PROJECT_DIR)/build/SwiftHaskell 454 | ``` 455 | 456 | Or, if you prefer building primarily with `stack build`, set the 457 | `build-type` in your `.cabal` to `Custom` and add a `postBuild` 458 | hook to `Setup.hs`: 459 | 460 | ```haskell 461 | import Distribution.Simple 462 | import System.Process 463 | 464 | main = defaultMainWithHooks $ simpleUserHooks 465 | { postBuild = \args buildFlags pkgDesc localBuildInfo -> do 466 | callProcess "bash" ["link-deps.sh"] 467 | callProcess "xcodebuild" ["-target", "SwiftHaskell"] 468 | } 469 | ``` 470 | 471 | ## Calling Haskell from Swift 472 | 473 | We're now ready to use exported Haskell functions from Swift. 474 | Import the module we defined in our `module.modulemap`, 475 | `SwiftHaskell`, at the top of `AppDelegate.swift`: 476 | 477 | ```swift 478 | import SwiftHaskell 479 | ``` 480 | 481 | Let's add a new label to the window for us to write the result 482 | of our Haskell function `square` into. Open `MainMenu.xib` and 483 | select the window object in the left sidebar to bring it into 484 | view. Then drag in a label from the object library in the right 485 | sidebar into the window: 486 | 487 | ![Add a label](tutorial/xcode-ib-add-label.png) 488 | 489 | Now option-click on `AppDelegate.swift` in the file list to open 490 | it in an assistant editor. Holding the control key, drag the 491 | label from the window into the `AppDelegate` class to add and 492 | connect a new `@IBOutlet`: 493 | 494 | ![Add and connect the label outlet](tutorial/xcode-ib-add-label-outlet.png) 495 | 496 | ![Name the outlet](tutorial/xcode-ib-label-outlet-dialog.png) 497 | 498 | Adding the outlet will add a new property to the `AppDelegate`: 499 | 500 | ```swift 501 | @IBOutlet weak var label: NSTextField! 502 | ``` 503 | 504 | With the `SwiftHaskell` module imported and a label connected, 505 | let's call `square` and display its result in the label. Add 506 | this to `applicationDidFinishLaunching`: 507 | 508 | ```swift 509 | label.stringValue = "\(square(5))" 510 | ``` 511 | 512 | The final contents of `AppDelegate.swift` are: 513 | 514 | ```swift 515 | import Cocoa 516 | import SwiftHaskell 517 | 518 | class AppDelegate: NSObject, NSApplicationDelegate { 519 | 520 | @IBOutlet weak var window: NSWindow! 521 | @IBOutlet weak var label: NSTextField! 522 | 523 | func applicationDidFinishLaunching(_ aNotification: Notification) { 524 | label.stringValue = "\(square(5))" 525 | } 526 | 527 | func applicationWillTerminate(_ aNotification: Notification) { 528 | } 529 | } 530 | ``` 531 | 532 | Running the app, 533 | 534 | ![5 squared](tutorial/squared.png) 535 | 536 | If the build fails with `Use of unresolved identifier 'square'`, 537 | perform a full clean with the *Product » Clean Build Folder...* 538 | ⌥⇧⌘K menu command and then rebuild. (Hold ⌥ option to reveal 539 | the menu item.) This appears to be a bug with Xcode (version 540 | 8.2 as of writing) caching some intermediate state from before 541 | the `SwiftHaskell` module was fully configured, and should not 542 | occur in future builds. 543 | 544 | ## Passing Complex Data Types 545 | 546 | ### Bytes 547 | 548 | #### `[UInt8]` to `ByteString` 549 | 550 | Call `withUnsafeBufferPointer` on a Swift `Array` to get 551 | an `UnsafeBufferPointer`, and then read its `.baseAddress` 552 | property to get an `UnsafePointer` pass into the exported 553 | Haskell function. The corresponding mutable variants are 554 | `withUnsafeMutableBufferPointer`, `UnsafeMutableBufferPointer`, 555 | and `UnsafeMutablePointer`. 556 | 557 | The generated Haskell headers use a single pointer type for all 558 | pointers, `HsPtr` (`void *`), which is mutable (not `const`). If 559 | you know that a function does not mutate through a pointer, you 560 | can use the `HsPtr(mutating:)` constructor to cast a non-mutable 561 | pointer to a mutable pointer. 562 | 563 | ```swift 564 | bytes.withUnsafeBufferPointer { bytesBufPtr in 565 | someHaskellFunction(HsPtr(mutating: bytesBufPtr.baseAddress), bytesBufPtr.count) 566 | } 567 | ``` 568 | 569 | If the function mutates the pointer's data, you must use 570 | `withUnsafeMutableBytes`: 571 | 572 | ```swift 573 | bytes.withUnsafeMutableBufferPointer { bytesBufPtr in 574 | someHaskellFunction(bytesBufPtr.baseAddress, bytesBufPtr.count) 575 | } 576 | ``` 577 | 578 | To bring an array of bytes into a Haskell `ByteString`, use 579 | `Data.ByteString.packCStringLen`: 580 | 581 | ```haskell 582 | type CString = Ptr CChar 583 | packCStringLen :: (CString, Int) -> IO ByteString 584 | ``` 585 | 586 | For example, 587 | 588 | ```haskell 589 | import Foreign.C 590 | import Foreign.Ptr 591 | 592 | import qualified Data.ByteString as B 593 | import Data.Word 594 | 595 | foreign export ccall countBytes :: Word8 -> Ptr CChar -> CSize -> IO CSize 596 | 597 | countBytes :: Word8 -> Ptr CChar -> CSize -> IO CSize 598 | countBytes needle haystack haystackLen = do 599 | s <- B.packCStringLen (haystack, fromIntegral haystackLen) 600 | pure (B.foldl (\count b -> count + if b == needle then 1 else 0) 0 s) 601 | ``` 602 | 603 | With a Swift wrapping function of 604 | 605 | ```swift 606 | func count(byte: UInt8, in bytes: [UInt8]) -> Int { 607 | var r = 0 608 | bytes.withUnsafeBytes { bytesPtr in 609 | r = Int(SwiftHaskell.countBytes(byte, HsPtr(mutating: bytesPtr.baseAddress))) 610 | } 611 | return r 612 | } 613 | ``` 614 | 615 | #### `ByteString` to `[UInt8]` 616 | 617 | To pass a `ByteString` to an exported Swift function that 618 | accepts a pointer and a length, use `useAsCStringLen`: 619 | 620 | ```haskell 621 | import Data.ByteString (ByteString) 622 | import qualified Data.ByteString as B 623 | 624 | foreign import ccall "someSwiftFunction" someSwiftFunction :: Ptr CChar -> CSize -> IO () 625 | 626 | passByteString :: ByteString -> IO () 627 | passByteString s = 628 | B.useAsCStringLen s $ \(p, n) -> 629 | someSwiftFunction p (fromIntegral n) 630 | ``` 631 | 632 | To return the contents of a `ByteString`, call `mallocArray` to 633 | allocate a new array with C's `malloc` allocator and copy the 634 | `ByteString` data into it. The Swift caller is then responsible 635 | for calling `free` on the pointer. Use `Foreign.Storable.poke` 636 | to also return the size by writing into a passed pointer. 637 | 638 | ```haskell 639 | import Data.ByteString (ByteString) 640 | import qualified Data.ByteString as B 641 | import qualified Data.ByteString.Unsafe as BU 642 | import Foreign.Storable (poke) 643 | 644 | mallocCopyByteString :: ByteString -> IO (Ptr CChar, Int) 645 | mallocCopyByteString s = 646 | BU.unsafeUseAsCStringLen s $ \(p, n) -> do 647 | a <- mallocArray n 648 | copyArray a p n 649 | pure (a, n) 650 | 651 | foreign export ccall getSequence :: Ptr CSize -> IO (Ptr CChar) 652 | 653 | getSequence :: Ptr CSize -> IO (Ptr CChar) 654 | getSequence sizePtr = do 655 | (p, n) <- mallocCopyByteString (B.pack [1..10]) 656 | poke sizePtr (fromIntegral n) 657 | pure p 658 | ``` 659 | 660 | The imported `getSequence` function returns a 661 | `UnsafeMutableRawPointer` in Swift. To copy the elements into 662 | a Swift array, first assign a type to the memory using the 663 | `.assumingMemoryBound(to:)` method. Then wrap the pointer 664 | and length in an `UnsafeBufferPointer` and pass it to the 665 | array constructor, which copies the elements into a new array 666 | using the `Collection` protocol that `UnsafeBufferPointer` 667 | implements. 668 | 669 | ```swift 670 | func getSequence() -> [UInt8] { 671 | var n = 0 672 | let p = SwiftHaskell.getSequence(&n).assumingMemoryBound(to: UInt8.self) 673 | let a = [UInt8](UnsafeBufferPointer(start: p, count: n)) 674 | free(p) 675 | return a 676 | } 677 | ``` 678 | 679 | ### Functions and Closures 680 | 681 | #### Passing Swift Functions to Haskell 682 | 683 | C function pointers have a type constructor of `FunPtr` in 684 | Haskell. For example, `FunPtr (CInt -> CSize -> IO ())` 685 | corresponds to `void (*)(int, size_t)`. 686 | 687 | To convert `FunPtr`s into callable Haskell functions, use a 688 | `foreign import ccall "dynamic"` declaration to ask the compiler 689 | to generate a conversion function for that function type: 690 | 691 | ```haskell 692 | foreign export ccall callbackExample :: FunPtr (CInt -> IO ()) -> IO () 693 | foreign import ccall "dynamic" unwrapCallback :: FunPtr (CInt -> IO ()) -> (CInt -> IO ()) 694 | 695 | callbackExample :: FunPtr (CInt -> IO ()) -> IO () 696 | callbackExample f = (unwrapCallback f) 3 697 | ``` 698 | 699 | If there is no context that needs to be captured, Swift 700 | functions can be passed in almost directly. However, like 701 | `HsPtr`, the generated headers only use a single function 702 | pointer type, `HsFunPtr` (`void (*)(void)`), so a little casting 703 | is usually necessary: 704 | 705 | ```swift 706 | func callbackExample(f: (@convention(c) (CInt) -> Void)) { 707 | let hsf: HsFunPtr = unsafeBitCast(f, to: HsFunPtr.self) 708 | SwiftHaskell.callbackExample(hsf) 709 | } 710 | ``` 711 | 712 | To pass Swift closures with context, we can use the traditional 713 | `void *` context pointer solution. Passing context however 714 | means that we need to keep it alive while the callback is held, 715 | and release it when we're done with it. For that, we can use 716 | `Foreign.ForeignPtr`. 717 | 718 | We'll wrap the context with 719 | 720 | ```haskell 721 | type FinalizerPtr a = FunPtr (Ptr a -> IO ()) 722 | newForeignPtr :: FinalizerPtr a -> Ptr a -> IO (ForeignPtr a) 723 | ``` 724 | 725 | and then apply it to the function with 726 | 727 | ```haskell 728 | withForeignPtr :: ForeignPtr a -> (Ptr a -> IO b) -> IO b 729 | ``` 730 | 731 | Together we have: 732 | 733 | ```haskell 734 | import Control.Concurrent 735 | import Foreign.C 736 | import Foreign.ForeignPtr 737 | import Foreign.Ptr 738 | 739 | foreign export ccall contextCallbackExample 740 | :: Ptr () 741 | -> FunPtr (Ptr () -> IO ()) 742 | -> FunPtr (Ptr () -> CInt -> IO ()) 743 | -> IO () 744 | foreign import ccall "dynamic" unwrapContextCallback 745 | :: FunPtr (Ptr () -> CInt -> IO ()) 746 | -> (Ptr () -> CInt -> IO ()) 747 | 748 | contextCallbackExample 749 | :: Ptr () -- ^ Context pointer 750 | -> FunPtr (Ptr () -> IO ()) -- ^ Context release function 751 | -> FunPtr (Ptr () -> CInt -> IO ()) -- ^ Callback function 752 | -> IO () 753 | contextCallbackExample ctxp releaseCtx callbackPtr = do 754 | ctxfp <- newForeignPtr releaseCtx ctxp 755 | let callback :: CInt -> IO () 756 | callback result = withForeignPtr ctxfp $ \ctxp' -> 757 | (unwrapContextCallback callbackPtr) ctxp' result 758 | _ <- forkIO $ do 759 | let result = 3 -- perform your complex computation here 760 | callback result 761 | pure () 762 | ``` 763 | 764 | The context pointer that we pass from the Swift side will be an 765 | object containing the closure itself. The function passed as 766 | the function pointer will merely cast the object to the known 767 | closure type and call it. 768 | 769 | To convert our Swift closure into a raw pointer, we'll use 770 | Swift's `Unmanaged` wrapper type. These are the methods we'll 771 | use from it: 772 | 773 | ```swift 774 | public struct Unmanaged { 775 | public static func passRetained(_ value: Instance) -> Unmanaged 776 | public func toOpaque() -> UnsafeMutableRawPointer 777 | public static func fromOpaque(_ value: UnsafeRawPointer) -> Unmanaged 778 | public func takeUnretainedValue() -> Instance 779 | public func takeRetainedValue() -> Instance 780 | } 781 | ``` 782 | 783 | Since Swift functions do not implement the `AnyObject` protocol 784 | (they are not class types), we'll need to wrap them in a object 785 | first. 786 | 787 | Additionally, referring directly to a Swift function name will 788 | give a Swift function type, which is not bit-compatible with a C 789 | function type. Before casting to `HsFunPtr`, we'll need to use a 790 | safe `as` cast to a `@convention(c)` type. 791 | 792 | ```swift 793 | func contextCallbackExample(f: ((CInt) -> Void)) { 794 | class Wrap { 795 | var inner: T 796 | 797 | init(_ inner: T) { 798 | self.inner = inner 799 | } 800 | } 801 | func release(context: HsPtr) { 802 | let _: Wrap<(CInt) -> Void> = Unmanaged.fromOpaque(context).takeRetainedValue() 803 | } 804 | func call(context: HsPtr, value: CInt) { 805 | let wf: Wrap<(CInt) -> Void> = Unmanaged.fromOpaque(context).takeUnretainedValue() 806 | let f = wf.inner 807 | f(value) 808 | } 809 | let release_hs = unsafeBitCast( 810 | release as @convention(c) (HsPtr) -> Void, to: HsFunPtr.self) 811 | let call_hs = unsafeBitCast( 812 | call as @convention(c) (HsPtr, CInt) -> Void, to: HsFunPtr.self) 813 | let ctx = Unmanaged.passRetained(Wrap(f)).toOpaque() 814 | SwiftHaskell.contextCallbackExample(ctx, release_hs, call_hs) 815 | } 816 | ``` 817 | 818 | #### Passing Haskell Functions to Swift 819 | 820 | In addition to the static `foreign export`, we can export 821 | dynamically created Haskell functions with `foreign export 822 | "wrapper"`. Unlike when passing Swift closures, a separate 823 | context pointer is not needed as the Haskell runtime supplies a 824 | distinct function pointer address for each wrapped function. 825 | 826 | ```haskell 827 | import Foreign.C 828 | import Foreign.Ptr 829 | 830 | foreign export ccall makeMultiplier :: CInt -> IO (FunPtr (CInt -> CInt)) 831 | foreign import ccall "wrapper" wrapMultiplier 832 | :: (CInt -> CInt) 833 | -> IO (FunPtr (CInt -> CInt)) 834 | 835 | makeMultiplier :: CInt -> IO (FunPtr (CInt -> CInt)) 836 | makeMultiplier x = wrapMultiplier (x *) 837 | ``` 838 | 839 | To free the `FunPtr`, export `Foreign.Ptr.freeHaskellFunPtr` and 840 | call it from Swift when you're done with the function. 841 | 842 | ```haskell 843 | foreign export ccall freeMultiplier :: FunPtr (CInt -> CInt) -> IO () 844 | 845 | freeMultiplier :: FunPtr (CInt -> CInt) -> IO () 846 | freeMultiplier = freeHaskellFunPtr 847 | ``` 848 | 849 | Wrap the Haskell function in a Swift class to manage its 850 | lifetime: 851 | 852 | ```swift 853 | class Multiplier { 854 | let funPtr: HsFunPtr 855 | 856 | init(_ x: CInt) { 857 | self.funPtr = SwiftHaskell.makeMultiplier(x) 858 | } 859 | 860 | func multiply(_ y: CInt) -> CInt { 861 | typealias F = @convention(c) (CInt) -> CInt 862 | let f = unsafeBitCast(self.funPtr, to: F.self) 863 | return f(y) 864 | } 865 | 866 | deinit { 867 | SwiftHaskell.freeMultiplier(self.funPtr) 868 | } 869 | } 870 | ``` 871 | 872 | ## Troubleshooting 873 | 874 | ### `stack build` fails with `ld: framework not found SwiftHaskellLibrary` 875 | 876 | Build the SwiftHaskellLibrary framework in Xcode before running 877 | `stack build`. 878 | 879 | ![Select the SwiftAppLibrary target](tutorial/xcode-select-framework-target.png) 880 | 881 | See the discussion about circular dependencies in the *Linking 882 | to the Executable* section. If this still fails, ensure that 883 | `link-deps.sh` is being run before the framework is built as 884 | described at the end of *Linking to the Executable*. Also check 885 | that the `build/` directory contains all three symlinks: 886 | 887 | - `SwiftAppLibrary.framework` `-> ~/Library/Developer/Xcode/DerivedData/SwiftHaskell-{hash}/Build/Products/{Debug,Release}/SwiftAppLibrary.framework` 888 | - `SwiftHaskell` `-> ../.stack-work/dist/{arch}/Cabal-{version}/build/SwiftHaskell/SwiftHaskell` 889 | - `ghc` 890 | - `include` `-> ~/.stack/programs/{arch}/ghc-{version}/bin/../lib/ghc-{version}/include` 891 | 892 | ### Building in Xcode fails with `PBXCp ...build/SwiftHaskell/SwiftHaskell: No such file or directory` 893 | 894 | Build the Haskell executable with `stack build`. 895 | 896 | ### Some Target Membership checkboxes are disabled in Xcode 897 | 898 | Add a Compile Sources or Copy Bundle Resources phase, as 899 | appropriate for the file you're modifying, to the target with 900 | the disabled checkbox. 901 | 902 | ### Building in Xcode fails with `Unable to run command ... - this target might include its own product.` 903 | 904 | Ensure that the Copy Files phase for the executable is copying 905 | the executable, and not the app bundle (the product). See the 906 | *App Bundle Configuration* section. 907 | 908 | ![Copy into Executables](tutorial/xcode-copy-files-swifthaskell-executable.png) 909 | 910 | ### Running the app fails with `dyld: Library not loaded: @rpath/libswiftAppKit.dylib` 911 | 912 | Ensure that **Always Embed Swift Standard Libraries** is set 913 | to **Yes** on the app target, as described in *App Bundle 914 | Configuration*. 915 | 916 | ![Always Embed Swift Standard Libraries: Yes](tutorial/xcode-embed-swift-standard-libs.png) 917 | 918 | ### Building in Xcode fails with `Use of unresolved identifier 'square'` 919 | 920 | If the module is certainly imported, this is probably from Xcode 921 | incorrectly retaining an expired cache for the `SwiftHaskell` 922 | module. Perform a full clean with the *Product » Clean Build 923 | Folder...* ⌥⇧⌘K menu command and then rebuild. (Hold ⌥ option to 924 | reveal the menu item.) 925 | -------------------------------------------------------------------------------- /SwiftAppLibrary/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | Default 541 | 542 | 543 | 544 | 545 | 546 | 547 | Left to Right 548 | 549 | 550 | 551 | 552 | 553 | 554 | Right to Left 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | Default 566 | 567 | 568 | 569 | 570 | 571 | 572 | Left to Right 573 | 574 | 575 | 576 | 577 | 578 | 579 | Right to Left 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | --------------------------------------------------------------------------------