removing files part of a later-added git ignore

Dear Future Jamie

When you add a gitignore to an existing project, to force git to release the files you don’t want, run the following from the directory where the project is located (note the extra period at the end of the 2st two commands)

1 git rm -r –cached . 2 3 git add . 4 5 git commit -am "Remove ignored files" 6

Love

Past Jamie

Creating an Initial Game Board

(Part 5 of the Panzer General Portable Project)

The first game-like task I wanted to accomplish was to set up the base game board – like I did here seven years ago.  I fired up the F# Xamarin Forms (XF) project I was working on last post and open the app.fs file.  Following the “Full-On Monty” Elm pattern, I am not going to do any of this app in markup so there are no XAML files to deal with.

Inside the app.fs file, I first created a record type to keep track of the tiles and units for each hex (later in this series, I will create a proper domain model – for now this is a just a spike to see what it takes to render the game board – I named it ContentData).  Notice that ContentData always had a Tile, Row and Column, but it does not always have a unit on it – so I used an option type. for Unit.  I also needed a type for the Tile, the Unit, and the Equipment – starting with image then moving out to additional data to make the game work.  Enter type providers – all I needed to do was to point to the files that were already part of the solution to get the types

1 type TileContext = JsonProvider<"Data//Scenario_Tile.json"> 2 type UnitContext = JsonProvider<"Data//Scenario_Unit.json"> 3 type EquipmentContext = JsonProvider<"Data//Equipment.json"> 4 type ContentData = {TileId: int; ColumnNumber: int; RowNumber: int; UnitId: int option} 5

I then created three functions to get the data for each instance of those types

1 let getTiles (scenarioId:int) = 2 let assembly = IntrospectionExtensions.GetTypeInfo(typeof<App>).Assembly 3 let stream = assembly.GetManifestResourceStream("scenariotile"); 4 let reader = new StreamReader(stream) 5 let json = reader.ReadToEnd() 6 let scenarioTile = TileContext.Parse(json) 7 scenarioTile.Dataroot.ScenarioTile 8 |> Array.filter(fun st -> st.ScenarioId = scenarioId) 9 10 let getUnits (scenarioId: int) = 11 let assembly = IntrospectionExtensions.GetTypeInfo(typeof<App>).Assembly 12 let stream = assembly.GetManifestResourceStream("scenariounit"); 13 let reader = new StreamReader(stream) 14 let json = reader.ReadToEnd() 15 let unit = UnitContext.Parse(json) 16 unit.Dataroot.ScenarioUnit 17 |> Array.filter(fun su -> su.ScenarioId = scenarioId) 18 19 let getEquipments = 20 let assembly = IntrospectionExtensions.GetTypeInfo(typeof<App>).Assembly 21 let stream = assembly.GetManifestResourceStream("equipment"); 22 let reader = new StreamReader(stream) 23 let json = reader.ReadToEnd() 24 let equipment = EquipmentContext.Parse(json) 25 equipment.Dataroot.Equipment

You first thought is “this screams refactoring to a high order function” and I would agree.  I will do that in next iteration

In any event, I needed a way to create the actual image from the file path and then a way of hydrating the ContentData

1 let createImage path = 2 let image = new Image() 3 image.Source <- ImageSource.FromResource(path) 4 image 5 6 let createTileContentData (tile:TileContext.ScenarioTile) (units: UnitContext.ScenarioUnit seq) (equipments: EquipmentContext.Equipment seq) = 7 let scenarioUnit = 8 units 9 |> Seq.tryFind(fun u -> u.StartingScenarioTileId = tile.ScenarioTileId) 10 let unitId = 11 match scenarioUnit with 12 | Some u -> 13 let equipment = equipments |> Seq.find(fun e -> e.EquipmentId = u.EquipmentId) 14 Some equipment.Icon 15 | None -> None 16 17 {TileId = tile.TerrainId; 18 ColumnNumber = tile.ColumnNumber; 19 RowNumber = tile.RowNumber; 20 UnitId = unitId}

With this function ready to return the image, I needed a way to put it in the correct place on the board.  In XF, this is accomplished via the Rectangle type

1 let createRectangle columnIndex rowIndex scale = 2 let height = 50.0 * scale 3 let width = 60.0 * scale 4 let yOffsetPlug = 25.0 * scale 5 let xOffsetPlug = -15.0 * scale 6 let columnIndex' = float columnIndex 7 let rowIndex' = float rowIndex 8 let yOffset = 9 match columnIndex % 2 = 0 with 10 | true -> yOffsetPlug 11 | false -> yOffsetPlug + yOffsetPlug 12 let xOffset = (columnIndex' * xOffsetPlug) + xOffsetPlug 13 let x = xOffset + columnIndex' * width 14 let y = yOffset + rowIndex' * height 15 new Rectangle(x,y,width,height)

With the individual hex functions ready, I was ready to put them onto the screen.  In XF, there is a type called layout that seems what I need to position images in absolute positions

1 let createLayout numberOfTiles scale = 2 let width = 60.0 * scale 3 let height = 50.0 * scale 4 let layout = new AbsoluteLayout() 5 layout.WidthRequest <- 12.0 * width 6 let rows = numberOfTiles % 20 |> float 7 layout.HeightRequest <- rows * height 8 layout

With the layout ready, I could create a function that takes in the layout, an individual hex’s contentdata, find it in the image files, and put it on the board.  I used the Children property of the Layout type to push multiple images on the same hex – I am not sure about Z-ordering yet, so I put the unit image after the tile

1 let addTileContent (layout:AbsoluteLayout) (contentData: ContentData) (scale: float) = 2 let rectangle = createRectangle contentData.ColumnNumber contentData.RowNumber scale 3 let terrainImageLocator = "tacmapdry" + contentData.TileId.ToString() 4 layout.Children.Add(createImage terrainImageLocator, rectangle) 5 match contentData.UnitId with 6 |Some i -> 7 let unitImageLocator = "tacicons" + i.ToString() 8 layout.Children.Add(createImage unitImageLocator, rectangle) 9 |None -> ()

With the individual hex functions ready, I could iterate through the first scenario and populate its initial board.  Notice that I put the Content control inside a ScrollView type that then put into a ContentPage that is then put into the Page.  I think the common convention is right – UX is very much OO in nature

1 let populateBoard = 2 let tiles = getTiles 0 3 let units = getUnits 0 4 let equipments = getEquipments 5 let numberOfTiles = tiles |> Seq.length 6 let scale = 1.0 7 let layout = createLayout numberOfTiles scale 8 tiles 9 |> Array.map(fun t -> createTileContentData t units equipments) 10 |> Array.iter(fun cd -> addTileContent layout cd scale) 11 let scrollView = new ScrollView() 12 scrollView.Orientation <- ScrollOrientation.Both 13 scrollView.Content <- layout 14 base.MainPage <- ContentPage(Content = scrollView) 15 16 do 17 populateBoard

So when I fired up the emulator, I got some pretty good first results

Screen Shot 2018-11-08 at 8.22.57 PM

Out of the box with Win Phone, I could pinch and spread a location and the screen images would automatically adjust and get bigger/smaller.  No such luck with XF – so I need to see how to do that.  In any event, zooming into the bottom left corner, all Panzer General fans will immediate recognize this layout. 

image

And it looks pretty good compared to the winphone

image

and the original game

image

Lots to do, but I am happy with the progress

Gist is here

Creating File Reference For An XF Shared .FSProj File

(Part 4 of the Panzer General Portable Project)

As I talked about in the last post, I have about 700 images to be referenced from the Panzer General Portable game.  When dealing with images in Xamarin Forms (XF), there are two choices:  make the image an embedded resource in the shared project or create a stub in the shared project and have the image in both the iOS and Android project.  Following Occam’s Razor, I opted for one file in the shared project.

Going back to the project, I opened the shared project and added the folder that contained all of the images.  A demo project looks like this:

Screen Shot 2018-11-08 at 6.15.01 PM

So far so good. 

I then high-lighted all of the images (that took a bit) and right clicked –> Build Action –> EmbeddedResource

Screen Shot 2018-11-08 at 6.16.20 PM

Visual Studio then tagged each of the files with a ResourceId of the file name:

Screen Shot 2018-11-08 at 6.15.29 PM

So far, so bad.  When I tried to reference these files in my code, the app could not find the images

So it turns out that when you are doing cross-platform development, Xamarin tries to reconcile any problems to the lowest level.  In this case, Android has some very specific opinions about naming and the file names that I used did not match.  iOS does not seem to have such limitations.   The frustrating thing for me is that Visual Studio for Mac let me assign illegal values and did not issue any warnings so it took way too long of trial and error to figure this out.  In my case, I used an underscore “_", which is an illegal character.

When I renamed a test image and followed the same steps, I got it working

Screen Shot 2018-11-08 at 6.15.48 PM

So I then went back and changed the naming of the photo parsing script.  But then I realized that highlighting all of the files in the solution explorer is a drag, so I wrote a new script that gave the correct file name and ResourceId for the fsproj file:

1 let createBlock (resource:string) (logicalName:string)= 2 ""\"><LogicalName>" + logicalName + "</LogicalName></EmbeddedResource>"

I then looped though all of the images files and created a text file

1 let getOutput path = 2 let resources = getResources path 3 let logicalNames = getLogicalNames path 4 resources 5 |> Array.zip logicalNames 6 |> Array.map(fun (r,ln) -> createBlock ln r) 7 |> Array.map(fun s -> " " + s)

the getLogicalNames function makes the ResourceId (Logical name in the .fsproj) acceptable to Android

1 let getLogicalNames path = 2 subDirectoryInfos path 3 |> Array.collect(fun sdi -> sdi.GetFiles()) 4 |> Array.map(fun f -> f.Name) 5 |> Array.map(fun n -> n.ToLowerInvariant()) 6 |> Array.map(fun n -> n.Replace(".jpeg",String.Empty)) 7 |> Array.map(fun n -> n.Replace("_",String.Empty))

I took the contents of that file, open the Panzer General project .fsproj file, pasted it in, and I got my images working

Gist is here

Parsing Photos

(Part 3 of the Panzer General Portable Project)

When I created Panzer General Portable (PGP) for the windows phone, I had to deal with creating a game board based on a a single image file that looks like this:

image

There are 12 rows and 20 columns of images – each image is 60 pixels wide and 50 pixels wide.  Each image represents a hex on the game board.  When I did the Windows Phone app, I loaded this image each time in needed a hex – not the most efficient use of resources to be sure.  In addition to these game board images, there are similar composite images of units, country flags, etc..  The game board is a series of overlaid images for a given hex. The application keeps track of the correct image to be used via the x/y coordinates of the hex.  For example, the top left fix has a x/y of 0,0.

image

 

In other parts of the app, an index is used so the 0,0 hex is also index 0 with the 1,19 hex having an index value of 20.

image

 

You will also note that each image is a rectangle, and the area outside of the hex is a pink color.  Apparently, back in the old days, that color meant “transparent” to windows.  However, it does not to the iOS and Android, so that color needs to be converted into transparent

I fired up a new FSharp project in Visual Studio for Mac and created a F# script

I created a function that takes in the path to this composite image on my file system, a target directory where I want the small image to be written to, the name of that file, and the x y coordinates to locate the smaller image in the composite image:

1 let createHexImage sourcePath targetDirectory (fileName:string) x y = 2 let width = 60 3 let height = 50 4 let index = x + (y * 20) 5 use sourceImage = Image.FromFile(sourcePath) 6 use bitmap = new Bitmap(width,height) 7 use graphics = Graphics.FromImage(bitmap) 8 let targetRectangle = new Rectangle(0,0,width,height) 9 let sourceRectangle = new Rectangle(width*x,height*y,width,height) 10 graphics.DrawImage(sourceImage,targetRectangle,sourceRectangle,GraphicsUnit.Pixel) 11 graphics.Flush() 12 let transparentBitmap = createTransparentBitmap bitmap 13 let fileName = fileName.Replace("_",String.Empty) 14 let fileName' = targetDirectory + "//" + fileName + index.ToString() + ".jpeg" 15 transparentBitmap.Save(fileName') 16

The code is pretty straight forward File I/O and image creation.  Notice on line 12 a function called “createTransparentBitmap” is called to turn the pink into transparent.  That function is actually step 0 of a function chain like this (BTW: read from the bottom up in F# world):

1 let updateColor (color:Color) = 2 match color.R, color.G, color.B with 3 | 255uy,225uy,225uy -> Color.FromArgb(0x00FFFFFF) 4 | _,_,_ -> color 5 6 let getCoordinates index (bitmap:Bitmap) = 7 let y = index / bitmap.Width 8 let x = index % bitmap.Width 9 x,y 10 11 let adjustColor index (bitmap:Bitmap) = 12 let coordinates = getCoordinates index bitmap 13 let x = fst coordinates 14 let y = snd coordinates 15 let color = bitmap.GetPixel(x,y) 16 let updatedColor = updateColor color 17 bitmap.SetPixel(x,y,updatedColor) 18 19 let createTransparentBitmap (bitmap:Bitmap) = 20 let totalPixels = bitmap.Height * bitmap.Width 21 [0 .. totalPixels - 1] 22 |> Seq.iter(fun i -> adjustColor i bitmap) 23 bitmap

  • updateColor looks at a given pixel and if it is “pink”, it returns a new pixel of transparent, else it returns the pixel that came in (note that System drawing uses Pixel and Color interchangeably, which can be confusing)
  • getCordinates gets the pixel from the x,y coordinate of the bitmap
  • adjustColor takes in a bitmap and an index – it uses the index to locate the targeted pixel, updates the color if needed, and then sets a new pixel into the bitmap with the updated pixel.
  • createTransparentBitmap takes in the bitmap, calculates the total pixels, creates an array of integers, and then calls the adjustColor function

So after running this script, you can see my file system has a list of all of the images that the game needs:

image

The gist is here

 

 

 

 

g

Setting Up The Environment For Mobile Development

(Part 2 of the Panzer General Portable Project)

I tried, I really did, to use my ultra-hyped personal computer running Win10 and Visual Studio 2017 to create a Xamarin phone app.  Things were actually OK until I tried to launch the emulator for iOS and Android.  The machine just fell on its face and after waiting several minutes for the simulator to launch… and then crash, I gave up.

Also, since you need a Mac to publish to the Apple store, it made sense to go out and get a MacBook.  I know that you can “rent a Mac” for Apple store deployment using services like Azure Dev Ops, but:

  • I would have to develop on a PC (see the paragraph above)
  • I couldn’t debug on a real device
  • Everyone, I mean everyone, I talked to about writing a phone app said to get a Mac to reduce friction
  • I could finally look like a hipster hacker at the local Starbucks

Getting started with a MacBook wasn’t particular hard – though I had to retrain some muscle memory for the keyboard shortcuts.  Visual Studio for Mac certainly has some quirks that are worth mentioning (some of which I documented here):

1) VS sits on top of XCode.  If XCode has an update, VS may not recognize it, so it will break.  A rule of thumb is to manually update XCode whenever there is a VS update, and manually update VS whenever there is an XCode Update

2) Managing files in the .fsproj file is not seamless.  F# requires the files to be in a certain order for dependency management which allows the awesomeness of inferred typing when compiling.  In Visual Studio for the PC, you can hold down the shift key and move your files up and down.  There is no such feature in VS for Mac so you need to open the .fsproj file and manually move things as demonstrated here

3) Some some random reason, my solution would stop building and I would get the following errors.

Screen Shot 2018-11-07 at 3.22.01 PM

I put it into User Voice here – until then, I just recreate a new project.  Obviously once I start coding for real, this is not a good solution,

Panzer General Portable: Redux

(Part 1 of the Panzer General Portable Project)

I recently had the need to come up to speed on build a phone app targeting both iOS and Android.  Since I have limited experience with Cordova and I much prefer writing F# over javascript, it made sense to take a look at what Xamarin has to offer.  Coincidently, I went to a meet up where Don Syme and Jim Bennett were presenting a new F# framework based on Elm called fabulous.  So what the heck – what not take Xamarin and fabulous out for a spin?  I decided to revisit the game I wrote for Windows Phone 7 called Panzer General Portable.  I was especially curious how the code would compare from the original C# + XML to F# + Xamarin Forms + fabulous.

Over the coming weeks, I plan to post my journey into this endeavor.  Hopefully, I can learn some things, contribute to the F#/fabulous community, and have some fun.

Nuget Location

Dear Future Jamie

When you want to find the location of the nuget packages for .NET Core, they are located here

C:\Users\[User]\.nuget\packages.

Love, Past Jamie

Replacing Entity Framework Classes With Record Types

I recently have been working on a mixed-language project (C# and F#) where the original implementation used the Entity Framework reverse POCO generator found here.  If you are not familiar, this Visual Studio add-in uses a T4 template to inspect a database and generate Entity Framework classes based on the tables that are located in the database.  The generator created two files for a given table: the entity and a class for configuration.

Capture

At the time, it probably was the best way to generate the hundreds of classes that were needed for the C# project.  However, now that we are introducing F# to the code base, it made sense to use the right tool for the job.

AFAIK, there are two ways you can use F# to do ORM.  Way #1 is to use type providers and way #2 is to have record types and hand-roll the connectivity.  I would have preferred to use a type provider to expose the database types in two lines of code, but since these types are being used by other C# and VB.NET projects, this was not possible.

Option 2 was to create record types.  Instead of hand writing the types, I decided to create a quick script that turns C# classes into F# types.  Since the each individual C# class was in its own file, the script traverses a directory and pull all of the C# files:

1 open System 2 open System.IO 3 open System.Collections.Generic 4 5 let path = @"C:\Git\..." 6 let folderInfo = System.IO.DirectoryInfo(path) 7 let files = folderInfo.GetFiles("*.cs")

Within each file, I needed to get both the type name and then the properties.  Getting the name was a text search for “public class” and the attributes was “get;set”

1 let parseClass (values: IEnumerable<string>) = 2 let className = 3 values 4 |> Seq.filter(fun l -> l.Contains("public class")) 5 let typeName = 6 match className |> Seq.length with 7 | 0 -> None 8 | _ -> Some (className |> Seq.head) 9 let propNames = 10 values 11 |> Seq.filter(fun l -> l.Contains("{ get; set; }")) 12 typeName, propNames

With the parsed values (1 class name and an array of attributes), I could then create the type name and the type attributes:

1 let createTypeName (className:string option) = 2 match className with 3 | Some cn -> 4 let typeName = cn.Replace(" public class"," ").Trim() 5 match(typeName.Contains("Configuration")) with 6 | true -> None 7 | false -> Some typeName 8 | None -> None 9 10 let reverseValues (typeAttribute:string) = 11 let tokens = typeAttribute.Split(' ') 12 match tokens.Length with 13 | 0 | 1 -> "" 14 | _ -> tokens.[1] + ":" + tokens.[0] 15 16 let createTypeAttributes (items: IEnumerable<string>) = 17 let temp = 18 items 19 |> Seq.map(fun i -> i.Replace("public","")) 20 |> Seq.map(fun i -> i.Replace("{ get; set; }","")) 21 |> Seq.map(fun i -> i, i.IndexOf("//")) 22 |> Seq.filter(fun (i,t) -> t > 0) 23 |> Seq.map(fun (i,t) -> i.Substring(0,t)) 24 |> Seq.map(fun i -> i.Trim()) 25 |> Seq.map(fun i -> reverseValues i) 26 match Seq.length temp with 27 | 0 -> "" 28 | _ -> Seq.reduce(fun acc elem -> acc + ";" + elem) temp

With those values, set, I could then create the types:

1 let createType (values:string option * IEnumerable<string>) = 2 createTypeName(fst values), 3 createTypeAttributes (snd values)

and then it was just a matter of putting it all together:

1 let printType (typeName:string) (typeAttributes: string) = 2 printfn "type %s {%s}" typeName typeAttributes 3 4 files 5 |> Array.map(fun f -> f.FullName) 6 |> Array.map(fun fn -> fn, File.ReadLines(fn)) 7 |> Array.map(fun (fn,c) -> parseClass c) 8 |> Array.map(fun x -> createType x) 9 |> Array.filter(fun (x,y) -> x.IsSome) 10 |> Array.map(fun (x,y) -> x.Value, y) 11 |> Array.iter(fun (x,y) -> printType x y)

With these values, I could create a single file and have all of my domain objects in 1 place – with about 95% less noise code.

Git is here

Creating Mobile Apps With Xamarin.Forms and Visual Studio For Mac Using F#

I have worked through chapters one to nine in Petzold’s Creating Mobile Apps with Xamarin.Forms using Visual Studio For the Mac and F#.  I found a couple of gotchas that might trip up someone doing the same that I thought I could document here.  I am using a Mac Book Pro and Visual Studio Enterprise For Mac 2017.

1) The code samples found on GitHub are in C# with a folder for the F# port – well until chapter nine when he stopped with port.  Since we are in a “Pull Request or STFU” world, I will be adding my samples for chapter 9 and beyond.

Issue #1

When you fire up Visual Studio for Mac and Select File –> New Solution –> Single View App (iOS –> app) and follow the default prompts, you will get a skeleton of a project.  Selecting Main.storyboard bring you to a designer that you can drag and drop controls onto an iPhone application, like a Label.  A problem arises when you try and given the Label a name like “testLabel”

Blog0

Just by setting the name, when you try and run your app, you get this error:

Objective-C exception thrown.  Name: NSUnknownKeyException Reason: [<ViewController 0x7f90f7a11310> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key testLabel.

The problem is that the IDE does not update the code-behind of the Main.Storyboard to reflect the new name.  If you open that file using “Source Code Editor”, you can then rename the id to “testLabel” from “name-outlet-197” and delete the destination attribute. 

Blog01

The project then runs

Issue #2

When you add a new File to an existing solution, in this case “MyPage”,

Screen Shot 2018-06-20 at 6.35.11 AM

 

it shows up alphabetically in the solution explorer

Blog04

If you try and reference that new file in the App.xaml to display it on startup, you get the following error:

Screen Shot 2018-06-20 at 6.37.17 AM

This is because in F#, the ordering of files is important.  MyPage has to be visible to the compiler before App.xaml.  The way to fix this is to open the project items file, in this case Greetings.projitems:

Blog05

Screen Shot 2018-06-20 at 6.39.08 AM

and change the new file (MyPage.fs) location from the default location (the end)

Screen Shot 2018-06-20 at 6.39.34 AM

to above the app.xaml fie

Screen Shot 2018-06-20 at 6.39.58 AM

 

Issue #3

Chapter 9 talks about calling native code on each device and referring to those calls from the forms project.  To achieve this, you need to add an interface to the common project

Blog09

and then refer to it in the common project page

Blog08

and to implement this in the iOS project

Blog07

I had to ask for help on Stack Overflow here.  Note that the “Dependency” attribute is in a module at the end, above the do() function.  I’ll be adding this code to the book’s repository in a bit, but at least this post will help others along.

The Social Transf#ormation of Software Development

This post is part of the F# Advent Calendar in English 2017 project. Check out all the other great posts there! And special thanks to Sergey Tihon for organizing this. (Also, thanks to Scott W from whom I copy/pasted the prior sentences.)

Since we are in the holiday season, it is appropriate that I spend some time reflecting on how awesome the FSharp language is. Also during this season, as we turn into a new year, it makes sense to look a bit into the future. Full disclaimer – I have no idea what I am talking about.

When I was an undergraduate, my advisor recommended The Social Transformation of American Medicine by Paul Starr. The book centers on American physicians and how they used social factors to achieve the professional autonomy and financial rewards that they have today. Indeed, there are few other professions that have such a powerful “moat” around their profession. I still remember a line that Starr used when describing how the AMA used the rule of law to crush non-certified physicians at the turn of the twentieth century: “Power abhors competition like nature abhors a vacuum” *

I recently re-read much of the book, spurred on by Uncle Bob Martin’s talk about professional autonomy that I saw at Skills Matter a couple of years ago (similar one here) . I am wondering how our professional will shake out – computer programmers are indispensable, in fact they are one of the few competitive advantages for companies*. Like it or not, all companies are software shops, the ones with good management get this and are adapting, the bad ones… well, they can always merge. Yet the average software engineer does not have the kind of compensation and autonomy that the average physician has. To be sure, there are some “rock stars”, but the averages do not compare.

So why don’t software engineers have such status? One can make an argument that correct software can be just as life-saving as a physician’s diagnosis, and potentially at a larger scale. So if it is not societal importance, what is it? Starr’s books has some interesting observations that lend itself to some interesting questions which would certainly increase the software developer’s status.

· Does the software industry need to have established practices that at enforced by the rule of law?

· Does anyone deploying software need to be licensed? There might be some minor logistical issues, but certainly doable (just tell VC it is a Blockchain use case)

· Does anyone writing software need to join an overall unified professional organization?

· Do we socialize politicians that “kids learn to code” is a dumb idea just like “kids learn to do surgery”?

· Do we educate business leaders of non-tech shops that the problem is not enough programmers but too many bad programmer? If they want better results, pay more and implement steps #1-#3 above. Does it then raise the possibility that their many management teams and associated skill set are pretty much irrelevant in a 21st century technology company?

· Does the industry tell computer programmers that can’t pass the muster for the 1st three bullet points that they need to find a different profession?

Which leads us to FSharp. FSharp is an awesome language that had a hard landing on the enterprise space. As our profession evolves, the market will have less influence. As far as I can see, the entire package of Windows, Visual Studio, .NET, C#, Desktop App, and Web Forms is coming to an end. Will newer languages like F# will get a serious look? I will be interested to see if the same regulatory forces that will lead to the end of the Mort will also pick and choose winning languages. God help us if they decide on javascript.

So what can we do in the FSharp community? Starr has some interesting lessons here too:

· Stick to our guns and continue to push for high-quality teams and code. Like physicians that turned away patients that would not follow their orders, avoid shops that treat development as a cost of business versus a strategic asset

· If you do work at a place like that, be an outspoken advocate for change.

· Get involved with startups and non-enterprise technical communities.

· Get involved with government – volunteer on boards, get to know your local politicians, spend some time raising FSharp awareness to key stakeholders

· Code Code Code. Keep your coding chops sharp.

In any event, I don’t know the answers to the above questions.  I don’t even know if they are the right questions.  To be sure, I maintain our profession is evolving and we do have some control of where it will end up.  With that in mind, hopefully the FSharp community can continue to be a force of positive change in our industry.

Onward to 2018!

 

** I find this line very useful in a variety of settings -> esp. in explaining American politics to friends from overseas

** IT does matter. It always has. The article was wrong from the moment it was published.